В Java 8, как я могу отфильтровать коллекцию, используя Stream
API, проверив отличимость свойства каждого объекта?
Например, у меня есть список Person
объектов, и я хочу удалить людей с тем же именем,
persons.stream().distinct();
Будет использовать проверку равенства по умолчанию для Person
объекта, поэтому мне нужно что-то вроде,
persons.stream().distinct(p -> p.getName());
К сожалению, distinct()
метод не имеет такой перегрузки. Person
Можно ли сделать это кратко, не изменяя проверку равенства внутри класса?
источник
Function<? super T, ?>
, а неFunction<? super T, Object>
. Также следует отметить, что для упорядоченного параллельного потока это решение не гарантирует, какой объект будет извлечен (в отличие от обычногоdistinct()
). Также для последовательных потоков есть дополнительные издержки при использовании CHM (чего нет в решении @nosid). Наконец, это решение нарушает контрактfilter
метода, предикат которого должен быть без состояния, как указано в JavaDoc. Тем не менее проголосовал.distinctByKey
не имеет представления о том, используется ли он в параллельном потоке. Он использует CHM, если он используется параллельно, хотя это добавляет накладные расходы в последовательном случае, как отметил Тагир Валеев выше.distinctByKey
. Но это работает, если вы вызываетеdistinctByKey
каждый раз, так что каждый раз создается новый экземпляр Predicate..filter(distinctByKey(...))
. Он выполнит метод один раз и вернет предикат. Таким образом, в основном карта уже используется повторно, если вы правильно используете ее в потоке. Если вы сделаете карту статичной, карта будет доступна всем пользователям. Таким образом, если у вас есть два потока, использующих этоdistinctByKey()
, оба будут использовать одну и ту же карту, а это не то, что вам нужно.CallSite
будет связанно сget$Lambda
методом - это будет возвращать новый экземплярPredicate
все время, но эти случаи будут один и те же ,map
иfunction
, насколько я понимаю. Очень хорошо!Альтернативой может быть размещение людей на карте с использованием имени в качестве ключа:
Обратите внимание, что лицо, которое сохраняется, в случае дублирования имени, будет первым зарегистрированным.
источник
distinct()
без накладных расходов? Как любая реализация узнает, видел ли она объект раньше, фактически не запоминая все различные значения, которые она видела? Так что накладные расходыtoMap
и,distinct
скорее всего, одинаковые.distinct()
создает сама верхняя часть.persons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
TreeSet
), который в любом случае уже различен илиsorted
в потоке, который также буферизует все элементы.Вы можете обернуть объекты человека в другой класс, который сравнивает только имена людей. После этого вы разворачиваете обернутые объекты, чтобы снова получить поток людей. Операции потока могут выглядеть следующим образом:
Класс
Wrapper
может выглядеть следующим образом:источник
equals
Метод может быть упрощен доreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Другое решение, использующее
Set
. Может быть не идеальное решение, но оно работаетИли, если вы можете изменить исходный список, вы можете использовать метод removeIf
источник
Существует более простой подход с использованием TreeSet с пользовательским компаратором.
источник
Мы также можем использовать RxJava (очень мощная библиотека реактивных расширений )
или
источник
Observable
на основе толчка, тогда как на основеStream
тяги. stackoverflow.com/questions/30216979/…Flux.fromIterable(persons).distinct(p -> p.getName())
Stream
API», а не «не обязательно используя stream». Тем не менее, это отличное решение проблемы XY фильтрации потока по различным значениям.Вы можете использовать
groupingBy
коллектор:Если вы хотите иметь другой поток, вы можете использовать это:
источник
Вы можете использовать
distinct(HashingStrategy)
метод в Eclipse Collections .Если вы можете
persons
выполнить рефакторинг для реализации интерфейса Коллекции Eclipse, вы можете вызвать метод непосредственно в списке.HashingStrategy - это просто интерфейс стратегии, который позволяет вам определять пользовательские реализации equals и hashcode.
Примечание: я являюсь коммиттером для Eclipse Collections.
источник
Я рекомендую использовать Vavr , если можете. С помощью этой библиотеки вы можете сделать следующее:
источник
Вы можете использовать библиотеку StreamEx :
источник
String
благодаря интернированию строк, но может и не работать .Расширяя ответ Стюарта Маркса, это можно сделать более коротким способом и без одновременной карты (если вам не нужны параллельные потоки):
Затем позвоните:
источник
Collections.synchronizedSet(new HashSet<>())
вместо него. Но это, вероятно, будет медленнее, чем сConcurrentHashMap
.Подобный подход, который использовал Саид Заринфам, но больше стиля Java 8 :)
источник
flatMap(plans -> plans.stream().findFirst().stream())
нее, чтобы избежать использования get on OptionalЯ сделал общую версию:
Пример:
источник
Другая библиотека, которая поддерживает это, является jOOλ , и ее
Seq.distinct(Function<T,U>)
метод:Под капотом он делает практически то же самое, что и принятый ответ .
источник
источник
Мой подход к этому состоит в том, чтобы сгруппировать все объекты с одинаковым свойством вместе, затем обрезать группы до размера 1 и, наконец, собрать их как
List
.источник
Список отдельных объектов можно найти с помощью:
источник
Самый простой способ реализовать это - перейти к функции сортировки, поскольку она уже предоставляет опцию,
Comparator
которая может быть создана с использованием свойства элемента. Затем вы должны отфильтровать дубликаты, что можно сделать с помощью statefull,Predicate
который использует тот факт, что для отсортированного потока все равные элементы смежны:Конечно, statefull
Predicate
не является потокобезопасным, однако, если вам это нужно, вы можете переместить эту логику в aCollector
и позволить потоку позаботиться о безопасности потоков при использовании вашегоCollector
. Это зависит от того, что вы хотите сделать с потоком различных элементов, которые вы не сказали нам в своем вопросе.источник
Основываясь на ответе @ josketres, я создал универсальный вспомогательный метод:
Вы можете сделать это более дружественным к Java 8, создав Collector .
источник
Может быть кому-нибудь пригодится. У меня было немного другое требование. Имея список объектов
A
от сторонних производителей, удалите все объекты, имеющие одинаковоеA.b
поле для одного и того жеA.id
(несколькоA
объектов с одинаковымиA.id
в списке). Ответ потока от Тагира Валеева вдохновил меня на использование обычая,Collector
который возвращаетMap<A.id, List<A>>
. ПростойflatMap
сделает все остальное.источник
У меня была ситуация, когда я должен был получить отдельные элементы из списка на основе 2 ключей. Если вы хотите различить на основе двух ключей или может составной ключ, попробуйте это
источник
В моем случае мне нужно было контролировать то, что было предыдущим элементом. Затем я создал предикат с отслеживанием состояния, в котором я контролировал, отличался ли предыдущий элемент от текущего элемента, в этом случае я сохранял его.
источник
Мое решение в этом списке:
В моей ситуации я хочу найти отличные значения и поместить их в список.
источник
Хотя ответ с наибольшим количеством голосов является абсолютно лучшим по сравнению с Java 8, в то же время он является абсолютно худшим с точки зрения производительности. Если вы действительно хотите плохое приложение с низкой производительностью, тогда используйте его. Простое требование извлечения уникального набора личных имен должно быть достигнуто просто «для каждого» и «набором». Ситуация становится еще хуже, если список превышает 10.
Предположим, у вас есть коллекция из 20 объектов, например:
Где ваш объект
SimpleEvent
выглядит так:И тест, у вас есть JMH подобного кода (Пожалуйста , обратите внимание, им , используя тот же distinctByKey Predicate упоминается в общепринятом ответа):
Тогда вы получите результаты Benchmark :
И, как вы можете видеть, простой For-Each в 3 раза лучше по пропускной способности и меньше по количеству ошибок по сравнению с Java 8 Stream.
Чем выше пропускная способность, тем выше производительность
источник
источник
Если вы хотите, чтобы список лиц, следующий будет простой способ
Кроме того, если вы хотите найти отдельный или уникальный список имен , а не Person , вы также можете использовать следующие два метода.
Способ 1: использование
distinct
Способ 2: использование
HashSet
источник
Person
s.Самый простой код, который вы можете написать:
источник