Похожая задача: Получить из строки только уникальные элементы
Стандартное быстрое решение задач такого рода: вычислить мапу частот для каждого элемента во входном списке; отфильтровать по значениям частоты 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;
}