Java 8 Відмінність за властивістю


456

Як у Java 8 можна фільтрувати колекцію за допомогою StreamAPI, перевіряючи виразність властивості кожного об'єкта?

Наприклад, у мене є список Personоб’єктів, і я хочу видалити людей з тим самим іменем,

persons.stream().distinct();

Буде використовувати перевірку рівності за замовчуванням для Personоб'єкта, тому мені потрібно щось на зразок,

persons.stream().distinct(p -> p.getName());

На жаль, distinct()метод не має такого перевантаження. Не змінюючи перевірку рівності всередині Personкласу, чи можна це зробити лаконічно?

Відповіді:


557

Вважайте, distinctщо це надзвичайний фільтр . Ось функція, яка повертає предикат, який підтримує стан про те, що він бачив раніше, і повертає, чи даний елемент бачили вперше:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}

Тоді ви можете написати:

persons.stream().filter(distinctByKey(Person::getName))

Зауважте, що якщо потік впорядкований і запускається паралельно, це збереже довільний елемент з числа дублікатів, а не перший, як і distinct()це.

(Це по суті те саме, що і моя відповідь на це питання: Java Lambda Stream Distinct () на довільному ключі? )


27
Думаю, для кращої сумісності аргумент повинен бути Function<? super T, ?>, ні Function<? super T, Object>. Також слід зазначити, що для впорядкованого паралельного потоку це рішення не гарантує, який об’єкт буде вилучений (на відміну від звичайного distinct()). Також для послідовних потоків є додаткові накладні витрати на використання CHM (який відсутній у розчині @nosid). Нарешті, це рішення порушує контракт filterметоду, який предикат повинен бути без громадянства, як зазначено в JavaDoc. Тим не менш, підтримується.
Тагір Валєєв

3
@java_newbie Екземпляр Predicate, який повертається, distinctByKeyне має уявлення про те, чи використовується він у паралельному потоці. Він використовує CHM у випадку, коли він використовується паралельно, хоча це додає накладні витрати в послідовному випадку, як Тагір Валєєв зазначив вище.
Стюарт Маркс

5
@holandaGo Це не вдасться, якщо ви збережете та повторно використаєте екземпляр Predicate, повернений distinctByKey. Але він працює, якщо ви викликаєте distinctByKeyкожен раз, так що він створює свіжий екземпляр Predicate кожен раз.
Стюарт Марк

3
@Chinmay ні, це не повинно. Якщо ви використовуєте .filter(distinctByKey(...)). Він виконає метод один раз і поверне присудок. Таким чином, карта вже використовується повторно, якщо ви правильно використовуєте її в потоці. Якщо ви зробите карту статичною, карта поділиться для всіх звичаїв. Отже, якщо у вас є два потоки, використовуючи це distinctByKey(), обидва будуть використовувати одну і ту ж карту, що не є тим, що ви хочете.
g00glen00b

3
Це так розумно і зовсім не очевидно. Як правило, це велика лямбда, і основне CallSiteбуде пов’язане з get$Lambdaметодом - який буде повертати новий примірник Predicateвесь час, але ці екземпляри поділять те саме mapі functionнаскільки я розумію. Дуже хороший!
Євген

152

Альтернативою може бути розміщення осіб на карті, використовуючи ім'я як ключове:

persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();

Зауважте, що особа, яка зберігається, у випадку дублюючого імені, буде першою приєднаною.


23
@skiwi: ви вважаєте, що існує спосіб реалізувати distinct()без цього накладних витрат? Як будь-яка реалізація може знати, якщо вона бачила об'єкт раніше, не запам'ятовуючи всі різні значення, які бачила? Тож накладні витрати toMapі distinct, швидше за все, однакові.
Хольгер

1
@Holger Я, можливо, помилявся там, оскільки я не думав, що над самими накладними distinct().
skiwi

2
І очевидно, це псує первісний порядок списку
Філіп

10
@Philipp: можна виправити, змінивши наpersons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
Holger

1
@DanielEarwicker це питання про "відмінність за майном". Потрібно буде сортувати потік за одним і тим же властивістю , щоб мати можливість ним скористатися. По-перше, ОП ніколи не заявляла, що потік сортується взагалі. По-друге, потоки не в змозі визначити, сортуються вони за певною властивістю . По-третє, немає справжньої операції потоку "відмінна за властивістю", щоб робити те, що ви пропонуєте. По-четверте, на практиці існує лише два способи отримати такий відсортований потік. Сортироване джерело ( TreeSet), яке так чи інакше вже відрізняється, або sortedв потоці, яке також буферизує всі елементи.
Холгер

101

Ви можете перенести об'єкти людини в інший клас, який лише порівнює імена осіб. Після цього ви розгортаєте загорнуті предмети, щоб знову повернути людину. Операції потоку можуть виглядати наступним чином:

persons.stream()
    .map(Wrapper::new)
    .distinct()
    .map(Wrapper::unwrap)
    ...;

Клас Wrapper може виглядати так:

class Wrapper {
    private final Person person;
    public Wrapper(Person person) {
        this.person = person;
    }
    public Person unwrap() {
        return person;
    }
    public boolean equals(Object other) {
        if (other instanceof Wrapper) {
            return ((Wrapper) other).person.getName().equals(person.getName());
        } else {
            return false;
        }
    }
    public int hashCode() {
        return person.getName().hashCode();
    }
}


5
@StuartCaie Насправді ... запам'ятовування немає, і справа не в продуктивності, а в адаптації до існуючого API.
Марко Топольник

6
com.google.common.base.Equivalence.wrap (S) та com.google.common.base.Equivalence.Wrapper.get () також може допомогти.
bjmi

Ви можете зробити клас обгортки загальним і параметризований функцією вилучення ключів.
Лій

equalsМетод може бути спрощений доreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Holger

55

Ще одне рішення, використовуючи Set. Можливо, це не ідеальне рішення, але воно працює

Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());

Або якщо ви можете змінити вихідний список, ви можете скористатися методом removeIf

persons.removeIf(p -> !set.add(p.getName()));

2
Це найкраща відповідь, якщо ви не використовуєте жодної сторонньої бібліотеки!
Маной Шреща

5
використовуючи геніальну ідею, що Set.add повертає true, якщо цей набір ще не містив вказаний елемент. +1
Luvie

Я вважаю, що цей метод не працює для паралельної обробки потоків, оскільки він не є безпечним для потоків.
LoBo

@LoBo Напевно, ні. Це просто ідея, яка підійде для простих випадків. Користувачі можуть розширити його для безпеки / паралельності потоку.
Сантош,

Цікавий підхід, але схожий на антидіаграму для зміни зовнішньої колекції (набору), фільтруючи потік в іншій колекції (осіб) ...
Джастін Роу

31

Існує простіший підхід із використанням TreeSet із спеціальним компаратором.

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName())) 
));

4
Я думаю, що ваша відповідь допомагає впорядкувати, а не до унікальності. Однак це допомогло мені сформулювати свої думки щодо того, як це зробити. Перевірте тут: stackoverflow.com/questions/1019854 / ...
janagn

Майте на увазі, що ви будете платити ціну за сортування елементів тут, і нам не потрібно сортування, щоб знайти дублікати або навіть видалити дублікати.
пісарук

12
Comparator.comparing (Person :: getName)
Жан-Франсуа Савар

24

Ми також можемо використовувати RxJava (дуже потужна реактивна бібліотека розширень )

Observable.from(persons).distinct(Person::getName)

або

Observable.from(persons).distinct(p -> p.getName())

Rx - приголомшливий, але це погана відповідь. Observableє поштовхом, тоді як на основі Streamтяги. stackoverflow.com/questions/30216979 / ...
sdgfsdh

4
питання задайте рішення java8, не обов'язково використовуючи stream. Моя відповідь показує, що java8 stream api менш потужний, ніж rx api
frhack

1
Використовуючи реактор , це будеFlux.fromIterable(persons).distinct(p -> p.getName())
Ritesh

Питання буквально говорить "за допомогою StreamAPI", а не "не обов'язково використовувати потік". Однак, це чудове рішення XY проблеми фільтрації потоку до різних значень.
М. Юстін

12

Ви можете використовувати groupingByколектор:

persons.collect(Collectors.groupingBy(p -> p.getName())).values().forEach(t -> System.out.println(t.get(0).getId()));

Якщо ви хочете мати інший потік, ви можете скористатися цим:

persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream().map(l -> (l.get(0)));

11

distinct(HashingStrategy)Метод можна використовувати в колекціях Eclipse .

List<Person> persons = ...;
MutableList<Person> distinct =
    ListIterate.distinct(persons, HashingStrategies.fromFunction(Person::getName));

Якщо ви можете рефактору personsреалізувати інтерфейс колекцій Eclipse Collections, ви можете зателефонувати методу безпосередньо у списку.

MutableList<Person> persons = ...;
MutableList<Person> distinct =
    persons.distinct(HashingStrategies.fromFunction(Person::getName));

HashingStrategy - це просто інтерфейс стратегії, який дозволяє визначати власні реалізації рівних та хеш-коду.

public interface HashingStrategy<E>
{
    int computeHashCode(E object);
    boolean equals(E object1, E object2);
}

Примітка. Я є членом колекції Eclipse.


Метод razloctBy був доданий у Eclipse Collections 9.0, що може додатково спростити це рішення. medium.com/@donraab/…
Дональд Рааб

10

Я рекомендую використовувати Vavr , якщо можете. З цією бібліотекою ви можете зробити наступне:

io.vavr.collection.List.ofAll(persons)
                       .distinctBy(Person::getName)
                       .toJavaSet() // or any another Java 8 Collection

Раніше відома як "javaslang" бібліотека.
користувач11153

9

Ви можете використовувати бібліотеку StreamEx :

StreamEx.of(persons)
        .distinct(Person::getName)
        .toList()

На жаль, той метод інакше дивовижної бібліотеки StreamEx погано розроблений - він порівнює рівність об'єктів замість використання рівних. Це може працювати для Strings завдяки інтернованому інтернету, але це також не може.
Крутний момент

7

Розширюючи відповідь Стюарта Маркса, це можна зробити коротшим способом і без паралельної карти (якщо вам не потрібні паралельні потоки):

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    final Set<Object> seen = new HashSet<>();
    return t -> seen.add(keyExtractor.apply(t));
}

Потім зателефонуйте:

persons.stream().filter(distinctByKey(p -> p.getName());

2
Цей не враховує, що потік може бути паралельним.
brunnsbe

Дякую за коментар, я оновив свою відповідь. Якщо вам не потрібен паралельний потік, не використання одночасних карт дає вам значно кращі показники.
Войцех Гурський

Ваш код, ймовірно, буде працювати для паралельних колекцій, якщо ви створили Collections.synchronizedSet(new HashSet<>())натомість. Але це, мабуть, буде повільніше, ніж із a ConcurrentHashMap.
Лій

7

Аналогічний підхід, який використовував Саїд Зарінфам, але більше стилю Java 8 :)

persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream()
 .map(plans -> plans.stream().findFirst().get())
 .collect(toList());

1
Я замінив би лінію на карту, щоб flatMap(plans -> plans.stream().findFirst().stream())уникнути використання
опції "

Можливо, це теж нормально: flatMap (плани -> plani.stream (). Limit (1))
Rrr

6

Я зробив загальну версію:

private <T, R> Collector<T, ?, Stream<T>> distinctByKey(Function<T, R> keyExtractor) {
    return Collectors.collectingAndThen(
            toMap(
                    keyExtractor,
                    t -> t,
                    (t1, t2) -> t1
            ),
            (Map<R, T> map) -> map.values().stream()
    );
}

Приклад:

Stream.of(new Person("Jean"), 
          new Person("Jean"),
          new Person("Paul")
)
    .filter(...)
    .collect(distinctByKey(Person::getName)) // return a stream of Person with 2 elements, jean and Paul
    .map(...)
    .collect(toList())



5

Мій підхід до цього полягає в тому, щоб згрупувати всі об'єкти з однаковою властивістю разом, потім скоротити групи до розміру 1, а потім нарешті зібрати їх як List.

  List<YourPersonClass> listWithDistinctPersons =   persons.stream()
            //operators to remove duplicates based on person name
            .collect(Collectors.groupingBy(p -> p.getName()))
            .values()
            .stream()
            //cut short the groups to size of 1
            .flatMap(group -> group.stream().limit(1))
            //collect distinct users as list
            .collect(Collectors.toList());

3

Список розрізнених об'єктів можна знайти за допомогою:

 List distinctPersons = persons.stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
                            ArrayList::new));

2

Найпростіший спосіб здійснити це - перейти на функцію сортування, оскільки вона вже надає необов'язковий варіант, Comparatorякий можна створити за допомогою властивості елемента. Тоді вам доведеться відфільтрувати дублікати, що можна зробити за допомогою стану, Predicateякий використовує той факт, що для відсортованого потоку всі рівні елементи суміжні:

Comparator<Person> c=Comparator.comparing(Person::getName);
stream.sorted(c).filter(new Predicate<Person>() {
    Person previous;
    public boolean test(Person p) {
      if(previous!=null && c.compare(previous, p)==0)
        return false;
      previous=p;
      return true;
    }
})./* more stream operations here */;

Звичайно, стан Predicateне є безпечним для потоків, однак, якщо це ваша потреба, ви можете перенести цю логіку в a Collectorі дозволити потоку дбати про безпеку потоку під час використання вашого Collector. Це залежить від того, що ви хочете зробити з потоком різних елементів, про які ви не сказали нам у своєму запитанні.


1

Спираючись на відповідь @ josketres, я створив загальний корисний метод:

Ви можете зробити це більш зручним для Java 8, створивши колектор .

public static <T> Set<T> removeDuplicates(Collection<T> input, Comparator<T> comparer) {
    return input.stream()
            .collect(toCollection(() -> new TreeSet<>(comparer)));
}


@Test
public void removeDuplicatesWithDuplicates() {
    ArrayList<C> input = new ArrayList<>();
    Collections.addAll(input, new C(7), new C(42), new C(42));
    Collection<C> result = removeDuplicates(input, (c1, c2) -> Integer.compare(c1.value, c2.value));
    assertEquals(2, result.size());
    assertTrue(result.stream().anyMatch(c -> c.value == 7));
    assertTrue(result.stream().anyMatch(c -> c.value == 42));
}

@Test
public void removeDuplicatesWithoutDuplicates() {
    ArrayList<C> input = new ArrayList<>();
    Collections.addAll(input, new C(1), new C(2), new C(3));
    Collection<C> result = removeDuplicates(input, (t1, t2) -> Integer.compare(t1.value, t2.value));
    assertEquals(3, result.size());
    assertTrue(result.stream().anyMatch(c -> c.value == 1));
    assertTrue(result.stream().anyMatch(c -> c.value == 2));
    assertTrue(result.stream().anyMatch(c -> c.value == 3));
}

private class C {
    public final int value;

    private C(int value) {
        this.value = value;
    }
}

1

Можливо, комусь стане в нагоді. У мене була ще одна вимога. Маючи список об'єктів Aвід сторонніх, видаліть усі, які мають одне і те ж A.bполе для одного A.id(декілька Aоб'єктів з однаковим A.idу списку). Відповідь розділу потоку Тагіра Валєєва надихнула мене на користування користувачем, Collectorякий повертається Map<A.id, List<A>>. Просте flatMapзробить все інше.

 public static <T, K, K2> Collector<T, ?, Map<K, List<T>>> groupingDistinctBy(Function<T, K> keyFunction, Function<T, K2> distinctFunction) {
    return groupingBy(keyFunction, Collector.of((Supplier<Map<K2, T>>) HashMap::new,
            (map, error) -> map.putIfAbsent(distinctFunction.apply(error), error),
            (left, right) -> {
                left.putAll(right);
                return left;
            }, map -> new ArrayList<>(map.values()),
            Collector.Characteristics.UNORDERED)); }

1

У мене виникла ситуація, коли я повинен був отримати окремі елементи зі списку на основі 2 клавіш. Якщо ви хочете відрізнятись на основі двох клавіш або комбінованого ключа, спробуйте це

class Person{
    int rollno;
    String name;
}
List<Person> personList;


Function<Person, List<Object>> compositeKey = personList->
        Arrays.<Object>asList(personList.getName(), personList.getRollno());

Map<Object, List<Person>> map = personList.stream().collect(Collectors.groupingBy(compositeKey, Collectors.toList()));

List<Object> duplicateEntrys = map.entrySet().stream()`enter code here`
        .filter(settingMap ->
                settingMap.getValue().size() > 1)
        .collect(Collectors.toList());

0

У моєму випадку мені потрібно було контролювати те, що було попереднім елементом. Потім я створив динамічний предикат , де я проконтрольовано , якщо попередній елемент відрізняється від поточного елемента, в цьому випадку я утримував його.

public List<Log> fetchLogById(Long id) {
    return this.findLogById(id).stream()
        .filter(new LogPredicate())
        .collect(Collectors.toList());
}

public class LogPredicate implements Predicate<Log> {

    private Log previous;

    public boolean test(Log atual) {
        boolean isDifferent = previouws == null || verifyIfDifferentLog(current, previous);

        if (isDifferent) {
            previous = current;
        }
        return isDifferent;
    }

    private boolean verifyIfDifferentLog(Log current, Log previous) {
        return !current.getId().equals(previous.getId());
    }

}

0

Моє рішення в цьому переліку:

List<HolderEntry> result ....

List<HolderEntry> dto3s = new ArrayList<>(result.stream().collect(toMap(
            HolderEntry::getId,
            holder -> holder,  //or Function.identity() if you want
            (holder1, holder2) -> holder1 
    )).values());

У моїй ситуації я хочу знайти чіткі цінності та внести їх у Список.


0

Хоча відповідь з найбільш високою оцінкою - це абсолютно найкраща відповідь wrt Java 8, вона в той же час є абсолютно гіршою з точки зору продуктивності. Якщо ви дійсно хочете, щоб програма мала низьку ефективність, тоді використовуйте її. Проста вимога вилучення унікального набору Особистих імен повинна бути досягнута простим «Для кожного» та «Набором». Справа стає ще гіршою, якщо список перевищує розмір 10.

Розглянемо, що у вас є колекція з 20 об’єктів, таких як:

public static final List<SimpleEvent> testList = Arrays.asList(
            new SimpleEvent("Tom"), new SimpleEvent("Dick"),new SimpleEvent("Harry"),new SimpleEvent("Tom"),
            new SimpleEvent("Dick"),new SimpleEvent("Huckle"),new SimpleEvent("Berry"),new SimpleEvent("Tom"),
            new SimpleEvent("Dick"),new SimpleEvent("Moses"),new SimpleEvent("Chiku"),new SimpleEvent("Cherry"),
            new SimpleEvent("Roses"),new SimpleEvent("Moses"),new SimpleEvent("Chiku"),new SimpleEvent("gotya"),
            new SimpleEvent("Gotye"),new SimpleEvent("Nibble"),new SimpleEvent("Berry"),new SimpleEvent("Jibble"));

Де ви заперечуєте, SimpleEventвиглядає так:

public class SimpleEvent {

private String name;
private String type;

public SimpleEvent(String name) {
    this.name = name;
    this.type = "type_"+name;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}
}

І для тестування у вас є такий JMH- код, як це, (Зверніть увагу, я використовую той самий окремий предикат, який згадується у прийнятій відповіді):

@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public void aStreamBasedUniqueSet(Blackhole blackhole) throws Exception{

    Set<String> uniqueNames = testList
            .stream()
            .filter(distinctByKey(SimpleEvent::getName))
            .map(SimpleEvent::getName)
            .collect(Collectors.toSet());
    blackhole.consume(uniqueNames);
}

@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public void aForEachBasedUniqueSet(Blackhole blackhole) throws Exception{
    Set<String> uniqueNames = new HashSet<>();

    for (SimpleEvent event : testList) {
        uniqueNames.add(event.getName());
    }
    blackhole.consume(uniqueNames);
}

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
            .include(MyBenchmark.class.getSimpleName())
            .forks(1)
            .mode(Mode.Throughput)
            .warmupBatchSize(3)
            .warmupIterations(3)
            .measurementIterations(3)
            .build();

    new Runner(opt).run();
}

Тоді ви будете мати Benchmark результати , як це:

Benchmark                                  Mode  Samples        Score  Score error  Units
c.s.MyBenchmark.aForEachBasedUniqueSet    thrpt        3  2635199.952  1663320.718  ops/s
c.s.MyBenchmark.aStreamBasedUniqueSet     thrpt        3   729134.695   895825.697  ops/s

І як ви бачите, простий For-Every втричі кращий за пропускною здатністю і менший за показник помилок порівняно з Java 8 Stream.

Чим вища пропускна здатність, тим краще продуктивність


1
Дякую, але запитання було дуже конкретно в контексті API Stream
RichK

Так, я згоден, я вже згадував "Хоча відповідь з найбільш високою оцінкою - абсолютно найкраща відповідь wrt Java 8". Проблему можна вирішити різними різними способами, і я намагаюся тут підкреслити, що проблему в руці можна вирішити просто, а не небезпечно за допомогою потоків Java 8, де небезпека погіршує продуктивність. :)
Абхінав Гангули

0
Here is the example
public class PayRoll {

    private int payRollId;
    private int id;
    private String name;
    private String dept;
    private int salary;


    public PayRoll(int payRollId, int id, String name, String dept, int salary) {
        super();
        this.payRollId = payRollId;
        this.id = id;
        this.name = name;
        this.dept = dept;
        this.salary = salary;
    }
} 

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class Prac {
    public static void main(String[] args) {

        int salary=70000;
        PayRoll payRoll=new PayRoll(1311, 1, "A", "HR", salary);
        PayRoll payRoll2=new PayRoll(1411, 2    , "B", "Technical", salary);
        PayRoll payRoll3=new PayRoll(1511, 1, "C", "HR", salary);
        PayRoll payRoll4=new PayRoll(1611, 1, "D", "Technical", salary);
        PayRoll payRoll5=new PayRoll(711, 3,"E", "Technical", salary);
        PayRoll payRoll6=new PayRoll(1811, 3, "F", "Technical", salary);
        List<PayRoll>list=new ArrayList<PayRoll>();
        list.add(payRoll);
        list.add(payRoll2);
        list.add(payRoll3);
        list.add(payRoll4);
        list.add(payRoll5);
        list.add(payRoll6);


        Map<Object, Optional<PayRoll>> k = list.stream().collect(Collectors.groupingBy(p->p.getId()+"|"+p.getDept(),Collectors.maxBy(Comparator.comparingInt(PayRoll::getPayRollId))));


        k.entrySet().forEach(p->
        {
            if(p.getValue().isPresent())
            {
                System.out.println(p.getValue().get());
            }
        });



    }
}

Output:

PayRoll [payRollId=1611, id=1, name=D, dept=Technical, salary=70000]
PayRoll [payRollId=1811, id=3, name=F, dept=Technical, salary=70000]
PayRoll [payRollId=1411, id=2, name=B, dept=Technical, salary=70000]
PayRoll [payRollId=1511, id=1, name=C, dept=HR, salary=70000]

-2

Якщо ви хочете, щоб список осіб був наступним, це був би простий спосіб

Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());

Крім того, якщо ви хочете знайти чіткий або унікальний список імен , а не Person , ви можете також скористатися наступними двома методами.

Спосіб 1: використання distinct

persons.stream().map(x->x.getName()).distinct.collect(Collectors.toList());

Спосіб 2: використання HashSet

Set<E> set = new HashSet<>();
set.addAll(person.stream().map(x->x.getName()).collect(Collectors.toList()));

2
Це створює список імен, а не Persons.
Халк

1
Це саме те, що я шукав. Мені знадобився метод єдиного рядка для усунення дублікатів при перетворенні колекції в інший. Дякую.
Радж

-3

Найпростіший код, який ви можете написати:

    persons.stream().map(x-> x.getName()).distinct().collect(Collectors.toList());

12
Це отримає чіткий перелік імен, але не Особи за іменем
RichK
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.