Создать новый список из исходного , который должен состоять из неповторяющихся чисел изначального списка

Рейтинг: 0Ответов: 2Опубликовано: 10.03.2023

Дан список целых чисел. Создать новый список, содержащий неповторяющиеся значения из исходного списка. Например изначальный список: 2 2 3 2 3 4 6 7 9 7 новый список: 4 6 9 Как это можно реализовать ? Вот метод , который у меня получился, но он делает вообще что-то не то

public static List<Integer> createNewList(List<Integer> list) {
        List<Integer> result = new ArrayList<>();
        result.add(list.get(0)); 
        for(int i=1;i<list.size();i++)
            if(list.get(i) != list.get(i-1)){
                result.add(list.get(i));
            }

        return result;
    }

Ответы

▲ 1

Похожая задача: Получить из строки только уникальные элементы

Стандартное быстрое решение задач такого рода: вычислить мапу частот для каждого элемента во входном списке; отфильтровать по значениям частоты 1; сформировать список из оставшихся ключей.

Решение со Stream API:

public static<T> List<T> getUniqueElementsStream(List<T> list) {
    return list.stream()
        .collect(Collectors.groupingBy(
            x -> x,
            LinkedHashMap::new, // Обеспечить порядок вставки как в исходном списке
            Collectors.summingInt(x -> 1) // или Collectors.counting()
        ))  // Map<T, Integer>
        .entrySet()
        .stream() // Stream<Map.Entry<T, Integer>>
        .filter(e -> e.getValue() == 1)
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());
}

Решение с циклами, Map::merge, Collection::removeIf (цикл можно заменить на list.forEach(item -> freq.merge(item, 1, Integer::sum)):

public static<T> List<T> getUniqueElementsFor(List<T> list) {
    Map<T, Integer> freq = new LinkedHashMap<>();
    // list.forEach(item -> freq.merge(item, 1, Integer::sum)); // вариант вместо цикла 
    for (T item : list) {
        freq.merge(item, 1, Integer::sum);
    }
    freq.values().removeIf(v -> v != 1); // удалить все неуникальные элементы
    return new ArrayList<>(freq.keySet());
}

Решение с использованием двух сетов и удалением дубликатов.
Основано на том, что метод Set::add возвращает false, если не произошло изменение сета, то есть обнаружен дубликат элемента:

public static<T> List<T> getUniqueElementsSet(List<T> list) {
    Set<T> ones = new LinkedHashSet<>();
    Set<T> dups = new HashSet<>();
    for (T item : list) {
        if (!ones.add(item)) {
            dups.add(item); // найден дубликат
        }
    }
    ones.removeAll(dups); // удалить все неуникальные элементы

    return new ArrayList<>(ones);
}

В вышеуказанных примерах входной список не изменяется, а создаётся новый список без дубликатов.


Если по какой-то причине нельзя пользоваться вычислением частот / множествами Set для обнаружения дубликатов, а только лишь "стандартными" методами, доступными для списков, то недавно была похожая задача: Удалить дубликаты из массива без использования Set и библиотечных методов, решения из которой нужно слегка модифицировать, чтобы удалить также элемент, для которого обнаружены дубликаты. В частности, придется создать копию входного списка, модифицировать её и вернуть как результат:

  • Решение с обычным циклом for
public static<T> List<T> keepUniques1(List<T> input) {
    List<T> list = new ArrayList<>(input);
    for (int i = 0; i < list.size(); i++) {
        T item = list.get(i);
        boolean dupFound = false;
        for (int j = i + 1; j < list.size(); j++) {
            if (Objects.equals(item, list.get(j))) {
                list.remove(j--); // коррекция индекса после удаления
                dupFound = true;
            }
        }
        if (dupFound) {
            list.remove(i--);
        }
    }
    return list;
}
  • Решение с использованием подсписков:
public static<T> List<T> keepUniques2(List<T> input) {
    List<T> list = new ArrayList<>(input);
    for (int i = 0; i < list.size(); i++) {
        T item = list.get(i);
        if (list.subList(i + 1, list.size()).removeIf(curr -> Objects.equals(item, curr))) {
            list.remove(i--);
        }
    }
    return list;
}
▲ -1

Достаточно просто создать Map(ключ - число, значение - количество вхождений) и потом пройтись по массиву, проверяя количество вхождений в нашей Map:

    private static List<Integer> createNewList(List<Integer> list) {
        //Создаём Map, подсчитывая количества вхождений чисел массива
        Map<Integer, Integer> counts = new HashMap<>();
        list.forEach((item) -> counts.put(item, counts.get(item) == null ? 1 : counts.get(item) + 1));
        //Возвращаем List, где value() в нашей Map = 1
        return list.stream()
                .filter(item -> counts.get(item) == 1)
                .toList();
    }