Создать новый список из исходного , который должен состоять из неповторяющихся чисел изначального списка
Дан список целых чисел. Создать новый список, содержащий неповторяющиеся значения из исходного списка. Например изначальный список: 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;
}
Ответы (2 шт):
Похожая задача: Получить из строки только уникальные элементы
Стандартное быстрое решение задач такого рода: вычислить мапу частот для каждого элемента во входном списке; отфильтровать по значениям частоты 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;
}
Достаточно просто создать 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();
}