IllegalStateException: «Дублирующий ключ» для Collectors.toMap () - PullRequest
0 голосов
/ 21 мая 2019

Справочный вопрос: Числа, составляющие Максимальную сумму

Я писал программу, которая печатала бы элементы, составляющие максимальную сумму. Мне удалось пройти через любой случайный сценарий, но когда моя максимальная сумма составляет два набора, мой код дает сбой.

Мой код:

class Ideone {
    public static void main(String[] args) throws java.lang.Exception {
        Scanner reader = new Scanner(System.in);
        int TestCases = reader.nextInt();
        reader.nextLine();
        String[] output = new String[TestCases];
        String sss = "";
        String ddd = "";

        for (int k = 0; k < TestCases; k++) {
            int noofELements = reader.nextInt();
            reader.nextLine();
            String[] al = reader.nextLine().split(" ");
            List<Integer> numbers = Arrays.stream(al).map(Integer::valueOf).collect(Collectors.toList());
            Ideone mm = new Ideone();
            String maxi = mm.maximumm(numbers, ddd);
            sss = sss.concat(maxi);

        }
        System.out.println(sss);

    }

    public String maximumm(List<Integer> numbers, String sss) {
        int toIndex = 3, fromIndex = 0;
        List<Integer> result = new ArrayList<>();
        while (toIndex < numbers.size()) {
            Map<Integer, Integer> map =
                IntStream.range(fromIndex, toIndex).mapToObj(i -> new AbstractMap.SimpleEntry<>(i, numbers.get(i)))
                    .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
            // find max of sublist
            int maxOfSub = numbers.subList(fromIndex, toIndex).stream().max(Integer::compareTo).get();
            //update indexes
            fromIndex = map.get(maxOfSub) + 2;
            toIndex += fromIndex;

            result.add(maxOfSub);
        }
        int lastMax = numbers.subList(fromIndex, numbers.size()).stream().max(Integer::compareTo).get();
        if (lastMax > 0) {
            result.add(lastMax);
        }
        result = result.stream().sorted(Integer::compareTo).collect(Collectors.toList());
        //System.out.println(result);
        sss = sss.concat(result.toString().replace(", ", "").replace("]", "").replace("[", ""));
        return sss;
        //  return result.stream().reduce(0,Integer::sum);
    }
}

Например, когда я даю ввод 4 5 4 3, тогда максимальная сумма несмежных элементов равна 8, что будет сделано из 4 4 или 5 3.

Мой полный код работает нормально, просто я не могу получить оба результата в своем конечном результате.

Мой журнал ошибок:

Исключение в потоке "main" java.lang.IllegalStateException: Duplicate ключ 0 в java.util.stream.Collectors.lambda $ throwingMerger $ 0 (Collectors.java:133) в java.util.HashMap.merge (HashMap.java:1254) в java.util.stream.Collectors.lambda $ toMap $ 58 (Collectors.java:1320) в java.util.stream.ReduceOps $ 3ReducingSink.accept (ReduceOps.java:169) на java.util.stream.IntPipeline $ 4 $ 1.accept (IntPipeline.java:250) на java.util.stream.Streams $ RangeIntSpliterator.forEachRemaining (Streams.java:110) в java.util.Spliterator $ OfInt.forEachRemaining (Spliterator.java:693) в java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:481) в java.util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:471) в java.util.stream.ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:708) в java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234) в java.util.stream.ReferencePipeline.collect (ReferencePipeline.java:499) в Ideone.maximumm (Ideone.java:47) в Ideone.main (Ideone.java:27)

Ошибка указывает на эту строку: result.add(maxOfSub);

Любая помощь была бы хороша:)

Ответы [ 3 ]

2 голосов
/ 22 мая 2019

Причина этой ошибки в том, что у вас есть дубликат <key> при вызове Stream.collect().Помните, этот метод выполняет изменяемую операцию сокращения над элементами потока.Поэтому, когда ваш вызов:

.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

Здесь вы определяете <keys> объекта Map как <values> из Entry<index, values>, определенного методом Stream.mapToObj().Теперь, когда в вашем тесте данных у вас есть 4, 5, 4, 3, это означает, что вы пытаетесь создать <key> для числа 4 дважды.Следовательно, вы получили это IllegalStateException.

Но как я могу это исправить?

Очень просто, просто измените определение вашего объекта Map с <values, indexes> на <indexes, values> вStream.collect() вызов. Как я могу это сделать? Ну, просто замените Map.Entry::getValue на Map.Entry::getKey и наоборот, как здесь:

.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Кроме того, есть лучший эквивалент с использованием IntStream.boxed() метод.Этот метод возвращает Stream, состоящий из элементов этого потока, каждый из которых упакован в Integer.Для меня это лучший способ преобразовать объект List в объект Map, например:

Map<Integer, Integer> map = IntStream
        .range(fromIndex, toIndex)
        .boxed()
        .collect(Collectors.toMap(i -> i, i -> numbers.get(i) > 0 ? numbers.get(i) : 0));

Обратите внимание, что я использую это выражение i -> numbers.get(i) > 0 ? numbers.get(i) : 0 для присвоения <values> изMap объект. Зачем вы это делаете? Потому что нам нужно отследить удаление отрицательных чисел, чтобы я заменил их на ноль.Метод Stream.filter() является альтернативой, но объект Map не будет содержать отфильтрованных элементов.

Однако это изменение повлияет на то, как вы обновляете свои индексы, потому что теперь значения карты равны <values>, а не <indexes>, как показано в этой строке:

fromIndex = map.getOrDefault(maxOfSub, toIndex - 1) + 2;

Toисправить это вам просто нужно конвертировать, получите <index> от corespondent <value> следующим образом:

fromIndex = IntStream
        .range(fromIndex, toIndex)
        .filter(i -> map.get(i).equals(maxOfSub))
        .findFirst()
        .orElse(toIndex - 1) + 2;

Альтернативное решение

Теперь, приведенная выше информация будет решать только IllegalStateException.Однако я обнаружил, что есть еще одна ошибка.Если я использую этот массив чисел 1, 9, 1, 7, 7, 5, 4, 1, 6, максимальная сумма несмежных чисел должна быть [9 + 7 + 5 + 6] = 27, но ваш код получил [9 + 7 + 6] = 22.Поэтому я попытался найти решение для этого здесь:

public class Ideone
{
    public static void main(String[] args)
    {
//        List<Integer> numbers = Arrays.asList(4, 5, 4, 3);
//        List<Integer> numbers = Arrays.asList(1, 9, 1, 7, 7, 5, 4, 1, 6);
        List<Integer> numbers = Arrays.asList(-1, 7, 8, -5, 4, 9, -2, 3);

        String sss = "";
        String ddd = "";
        Ideone mm = new Ideone();

        List<List<Integer>> maxi = mm.findMaxSumNonAdjacentStream(numbers, numbers.size());
        System.out.println(Collections.singletonList(maxi));

    }

    public List<List<Integer>> findMaxSumNonAdjacentStream(List<Integer> numbers, int size)
    {
        int fromIndex = 0;

        Map<Integer, Integer> maxSumMap = IntStream
                .range(fromIndex, size)
                .boxed()
                .collect(Collectors.toMap(i -> i, i -> numbers.get(i) > 0 ? numbers.get(i) : 0));

        Map<Integer, List<Integer>> indexMap = IntStream
                .range(fromIndex, size)
                .mapToObj(i -> new AbstractMap.SimpleEntry<>(i, Collections.singletonList(numbers.get(i))))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        maxSumMap.replace(1, Math.max(numbers.get(1), numbers.get(0)));

        List<Integer> maxValList = maxSumMap
                .entrySet()
                .stream()
                .filter(entry -> entry.getKey() > 1)
                .map(entry -> {
                    int index = entry.getKey();
                    int prevOne = index - 1;
                    int prevTwo = index - 2;
                    int prevValOne = maxSumMap.getOrDefault(prevOne, 0);
                    int prevValTwo = maxSumMap.getOrDefault(prevTwo, 0);

                    int maxVal = Math.max(prevValOne, prevValTwo + entry.getValue());
                    boolean exclude = prevValOne > (prevValTwo + entry.getValue());

                    List<Integer> elements = new ArrayList<>();
                    if (prevValOne > 0 && exclude) {
                        elements = new ArrayList<>(indexMap.get(prevOne));
                    } else if (prevValTwo > 0 && !exclude) {
                        elements = new ArrayList<>(indexMap.get(prevTwo));
                    }

                    if (!exclude) {
                        elements.add(entry.getValue());
                        elements = elements.stream().sorted(Integer::compareTo).collect(Collectors.toList());
                    }

                    maxSumMap.replace(index, maxVal);
                    indexMap.replace(index, elements);

                    return index;
                })
                .collect(Collectors.toList());

        Integer max = maxValList
                .stream()
                .mapToInt(v -> v)
                .max().orElseThrow(NoSuchElementException::new);

        int lastMax = maxValList.stream().max(Integer::compareTo).orElse(-1);
        Integer maxVal = maxSumMap.get(max);

        List<Integer> result = maxSumMap
                .entrySet()
                .stream()
                .filter(entry -> entry.getValue().equals(maxVal))
                .map(i -> i.getKey())
                .collect(Collectors.toList());

        Predicate<Map.Entry<Integer, List<Integer>>> containMaxList =
                mapEntry -> result.contains(mapEntry.getKey());

        return indexMap.entrySet()
                .stream()
                .filter(containMaxList)
                .map(i -> i.getValue())
                .collect(Collectors.toList());
    }
}
0 голосов
/ 21 мая 2019

Есть несколько дубликатов <keys> в ваших значениях. Посмотрите:

Map<Integer, Integer> map =
                IntStream.range(fromIndex, toIndex).mapToObj(i -> new AbstractMap.SimpleEntry<>(i, numbers.get(i)))
                    .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

Как это решить

Вы обменяете key-value в коде Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey), поэтому вы получили дублирующую ошибку.

0 голосов
/ 21 мая 2019

Вы пытаетесь построить обратную карту из карты с дублирующимися значениями, что приводит к дублированию ключей.Вы должны избегать этого.

Карта типа

{0: 4, 1: 5, 2: 4, 3: 3}

не может быть инвертирована в

{4: 0, 5: 1, 4: 2, 3: 3}

, потому что тогда будет map.get (4)?4 или 0?

Ошибка возникает в

// 1)
IntStream.range(fromIndex, toIndex)
    // 2)
    .mapToObj(i -> new AbstractMap.SimpleEntry<>(i, numbers.get(i)))
    // 3)
    .collect(Collectors.toMap(
                 Map.Entry::getValue, 
                 Map.Entry::getKey));
  • 1) Сначала вы создаете поток диапазона int (0, 1, 2, 3)
  • 2) затем вы создаете поток SimpleEntry ((0: numbers[0]), (1: numbers[1]), (2: numbers[2]))
  • 3) затем вы пытаетесь инвертировать это как карту:

    {numbers [0]: 0, numbers [1]: 1 ...}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...