Java 8 Distinto per proprietà


456

In Java 8 come posso filtrare una raccolta usando il Stream API controllando la nitidezza di una proprietà di ciascun oggetto?

Ad esempio, ho un elenco di Personoggetti e desidero rimuovere le persone con lo stesso nome,

persons.stream().distinct();

Utilizzerò il controllo di uguaglianza predefinito per un Personoggetto, quindi ho bisogno di qualcosa come

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

Sfortunatamente il distinct()metodo non ha questo sovraccarico. Senza modificare il controllo di uguaglianza all'interno della Personclasse è possibile farlo in modo succinto?

Risposte:


557

Considera distinctdi essere un filtro con stato . Ecco una funzione che restituisce un predicato che mantiene lo stato su ciò che è stato visto in precedenza e che restituisce se l'elemento dato è stato visto per la prima volta:

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

Quindi puoi scrivere:

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

Si noti che se lo stream viene ordinato e viene eseguito in parallelo, ciò preserverà un elemento arbitrario tra i duplicati, anziché il primo, come distinct()fa.

(Questa è essenzialmente la mia risposta a questa domanda: Java Lambda Stream Distinct () su chiave arbitraria? )


27
Immagino che per una migliore compatibilità l'argomento dovrebbe essere Function<? super T, ?>, no Function<? super T, Object>. Inoltre, va notato che per il flusso parallelo ordinato questa soluzione non garantisce quale oggetto verrà estratto (a differenza del normale distinct()). Anche per i flussi sequenziali c'è un sovraccarico aggiuntivo sull'uso di CHM (che è assente nella soluzione @nosid). Infine, questa soluzione viola il contratto di filtermetodo che il predicato deve essere apolide come indicato in JavaDoc. Tuttavia votato.
Tagir Valeev,

3
@java_newbie L'istanza Predicate restituita distinctByKeynon ha idea del fatto che venga utilizzata in un flusso parallelo. Utilizza CHM nel caso in cui venga utilizzato in parallelo, anche se questo aggiunge un sovraccarico nel caso sequenziale, come notato sopra da Tagir Valeev.
Stuart segna il

5
@holandaGo Fallirà se salvi e riutilizzi l'istanza Predicate restituita da distinctByKey. Ma funziona se chiami distinctByKeyogni volta, in modo da creare una nuova istanza del predicato ogni volta.
Stuart segna il

3
@Chinmay no, non dovrebbe. Se usi .filter(distinctByKey(...)). Eseguirà il metodo una volta e restituirà il predicato. Quindi, in pratica, la mappa viene già riutilizzata se la si utilizza correttamente all'interno di uno stream. Se si desidera rendere statica la mappa, la mappa verrà condivisa per tutti gli usi. Quindi, se hai due flussi che usano questo distinctByKey(), entrambi utilizzerebbero la stessa mappa, che non è quello che vuoi.
g00glen00b,

3
Questo è talmente intelligente e completamente non ovvio. Generalmente si tratta di un lambda con stato e il sottostante CallSitesarà collegato al get$Lambdametodo, che restituirà sempre una nuova istanza Predicate, ma tali istanze condivideranno lo stesso mape functionper quanto ho capito. Molto bella!
Eugene,

152

Un'alternativa sarebbe quella di posizionare le persone in una mappa usando il nome come chiave:

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

Si noti che la persona che viene mantenuta, in caso di un nome duplicato, sarà la prima incantata.


23
@skiwi: pensi che ci sia un modo per implementare distinct()senza quel sovraccarico? Come può una implementazione sapere se ha già visto un oggetto senza ricordare effettivamente tutti i valori distinti che ha visto? Quindi il sovraccarico di toMaped distinctè molto probabilmente lo stesso.
Holger,

1
@Holger Potrei essermi sbagliato lì perché non avevo pensato alla creazione dello distinct()stesso overhead .
Skiwi,

2
E ovviamente rovina l'ordine originale dell'elenco
Philipp

10
@Philipp: potrebbe essere corretto cambiando inpersons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
Holger il

1
@DanielEarwicker questa domanda riguarda "distinti per proprietà". Richiederebbe che il flusso fosse ordinato in base alla stessa proprietà , per poterne usufruire. Innanzitutto, l'OP non ha mai dichiarato che lo stream è stato ordinato affatto. In secondo luogo, i flussi non sono in grado di rilevare se sono ordinati in base a una determinata proprietà . Terzo, non esiste alcuna vera operazione di flusso "distinto per proprietà" per fare ciò che suggerisci. In pratica, in pratica, ci sono solo due modi per ottenere un flusso così ordinato. Una fonte ordinata ( TreeSet) che è già distinta comunque o sortedsullo stream che buffer anche tutti gli elementi.
Holger,

101

Puoi avvolgere gli oggetti persona in un'altra classe, che confronta solo i nomi delle persone. Successivamente, scartate gli oggetti avvolti per ottenere nuovamente un flusso di persone. Le operazioni del flusso potrebbero apparire come segue:

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

La classe Wrapperpotrebbe apparire come segue:

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();
    }
}

13
Questa è chiamata la trasformata di Schwartz
Stuart Caie,

5
@StuartCaie Non proprio ... non c'è memoization, e il punto non è la prestazione, ma l'adattamento all'API esistente.
Marko Topolnik,

6
com.google.common.base.Equivalence.wrap (S) e com.google.common.base.Equivalence.Wrapper.get () potrebbero essere di aiuto.
Bjmi,

È possibile rendere la classe wrapper generica e parametrizzata da una funzione di estrazione della chiave.
Lii,

Il equalsmetodo può essere semplificato perreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Holger,

55

Un'altra soluzione, utilizzando Set. Potrebbe non essere la soluzione ideale, ma funziona

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

Oppure, se è possibile modificare l'elenco originale, è possibile utilizzare il metodo removeIf

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

2
Questa è la risposta migliore se non stai utilizzando librerie di terze parti!
Manoj Shrestha,

5
usando l'idea geniale che Set.add restituisce vero se questo set non conteneva già l'elemento specificato. +1
Luvie,

Credo che questo metodo non funzioni per l'elaborazione di flussi paralleli, in quanto non è thread-safe.
LoBo

@LoBo Probabilmente no. Questa è solo un'idea, che funzionerà per casi semplici. Gli utenti possono estenderlo per la sicurezza / il parallelismo dei thread.
Santhosh,

Approccio interessante, ma sembra un po 'come un anti-pattern per modificare una collezione esterna (set) mentre filtra un flusso su un'altra collezione (persone) ...
Justin Rowe

31

Esiste un approccio più semplice utilizzando un TreeSet con un comparatore personalizzato.

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

4
Penso che la tua risposta aiuti verso l'ordinamento e non verso l'unicità. Tuttavia, mi ha aiutato a impostare i miei pensieri su come farlo. Controllare qui: stackoverflow.com/questions/1019854/...
janagn

Tieni presente che pagherai il prezzo per ordinare gli elementi qui e non abbiamo bisogno di ordinare per trovare duplicati o addirittura rimuovere duplicati.
pisaruk,

12
Comparator.comparing (Person :: getName)
Jean-François Savard,

24

Possiamo anche usare RxJava ( libreria di estensioni reattive molto potente )

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

o

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

Rx è fantastico, ma questa è una risposta scadente. Observableè basato su push mentre Streamè basato su pull. stackoverflow.com/questions/30216979/...
sdgfsdh

4
la domanda richiede una soluzione java8 che non utilizza necessariamente stream. La mia risposta mostra che java8 stream api è meno potente di rx api
frhack

1
Usando il reattore , saràFlux.fromIterable(persons).distinct(p -> p.getName())
Ritesh il

La domanda dice letteralmente "usare l' StreamAPI", non "non usare necessariamente lo stream". Detto questo, questa è un'ottima soluzione al problema XY di filtrare il flusso su valori distinti.
M. Justin,

12

Puoi usare il groupingByraccoglitore:

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

Se vuoi avere un altro stream puoi usare questo:

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

11

È possibile utilizzare il distinct(HashingStrategy)metodo nelle raccolte Eclipse .

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

Se è possibile effettuare il refactoring personsper implementare un'interfaccia Eclipse Collections, è possibile chiamare il metodo direttamente nell'elenco.

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

HashingStrategy è semplicemente un'interfaccia strategica che ti consente di definire implementazioni personalizzate di uguali e hashcode.

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

Nota: sono un committer per le raccolte Eclipse.


Il metodo distintoBy è stato aggiunto in Eclipse Collections 9.0 che può semplificare ulteriormente questa soluzione. medium.com/@donraab/…
Donald Raab,

10

Consiglio di usare Vavr , se puoi. Con questa libreria è possibile effettuare le seguenti operazioni:

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

Precedentemente noto come libreria "javaslang".
user11153

9

Puoi usare la libreria StreamEx :

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

Sfortunatamente, quel metodo della libreria StreamEx altrimenti eccezionale è mal progettato: confronta l'uguaglianza degli oggetti invece di usare uguali. Questo può funzionare per Strings grazie al string interning, ma potrebbe anche non esserlo.
Coppia

7

Estendendo la risposta di Stuart Marks, questo può essere fatto in modo più breve e senza una mappa simultanea (se non hai bisogno di flussi paralleli):

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

Quindi chiama:

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

2
Questo non tiene conto del fatto che il flusso potrebbe essere parallelo.
Brunnsbe,

Grazie per il commento, ho aggiornato la mia risposta. Se non hai bisogno di un flusso parallelo, non usare mappe simultanee ti dà prestazioni molto migliori.
Wojciech Górski,

Il tuo codice probabilmente funzionerebbe per raccolte parallele se invece ne creassi uno Collections.synchronizedSet(new HashSet<>()). Ma probabilmente sarebbe più lento che con a ConcurrentHashMap.
Lii,

7

Approccio simile usato da Saeed Zarinfam ma più stile Java 8 :)

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

1
Sostituirei la linea della mappa con flatMap(plans -> plans.stream().findFirst().stream())essa per evitare l'uso dell'accesso su Opzionale
Andrew Sneck,

Forse anche questo è ok: flatMap (plan -> plan.stream (). Limit (1))
Rrr

6

Ho realizzato una versione generica:

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()
    );
}

Un esempio:

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

Il mio approccio a questo è di raggruppare tutti gli oggetti con la stessa proprietà insieme, quindi tagliare i gruppi alla dimensione di 1 e infine raccoglierli come a 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

L'elenco di oggetti distinti può essere trovato usando:

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

2

Il modo più semplice per implementarlo è saltare sulla funzione di ordinamento in quanto fornisce già un optional Comparatorche può essere creato usando la proprietà di un elemento. Quindi devi filtrare i duplicati che possono essere fatti usando un statefull Predicateche usa il fatto che per un flusso ordinato tutti gli elementi uguali sono adiacenti:

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 */;

Ovviamente, un statefull Predicatenon è thread-safe, tuttavia se è questa la tua necessità puoi spostare questa logica in a Collectore lasciare che lo stream si occupi della sicurezza del thread quando usi il tuo Collector. Questo dipende da cosa vuoi fare con il flusso di elementi distinti che non ci hai detto nella tua domanda.


1

Sulla base della risposta di @ josketres, ho creato un metodo di utilità generico:

Puoi renderlo più compatibile con Java 8 creando un Collector .

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

Forse sarà utile per qualcuno. Avevo un altro requisito. Avere un elenco di oggetti Adi terze parti rimuove tutti quelli che hanno lo stesso A.bcampo per lo stesso A.id(più Aoggetti con lo stesso A.idnell'elenco). La risposta alla partizione in streaming di Tagir Valeev mi ha ispirato a usare la personalizzazione Collectorche ritorna Map<A.id, List<A>>. Semplice flatMapfarà il resto.

 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

Ho avuto una situazione in cui supponevo di ottenere elementi distinti dalla lista in base a 2 chiavi. Se si desidera distinguere in base a due chiavi o è possibile utilizzare una chiave composita, provare questo

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

Nel mio caso avevo bisogno di controllare quale fosse l'elemento precedente. Ho quindi creato un predicato con stato in cui ho controllato se l'elemento precedente era diverso dall'elemento corrente, in quel caso l'ho tenuto.

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

La mia soluzione in questo elenco:

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());

Nella mia situazione, voglio trovare valori distinti e inserirli nell'elenco.


0

Mentre la risposta con il voto più alto è la risposta assolutamente migliore rispetto a Java 8, è allo stesso tempo assolutamente peggiore in termini di prestazioni. Se vuoi davvero un'applicazione cattiva a basse prestazioni, vai avanti e usala. Il semplice requisito di estrarre un insieme univoco di nomi di persona deve essere raggiunto con un semplice "Per-ciascuno" e un "insieme". Le cose peggiorano ulteriormente se l'elenco ha dimensioni superiori a 10.

Considera di avere una raccolta di 20 oggetti, in questo modo:

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"));

Dove l'oggetto SimpleEventappare così:

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;
}
}

E per testare, hai codice JMH come questo, (Nota, sto usando lo stesso distinto Predicato di KeyKey menzionato nella risposta accettata):

@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();
}

Quindi otterrai risultati di benchmark come questo:

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

E come puoi vedere, un semplice For-Each ha una resa 3 volte migliore e un punteggio di errore inferiore rispetto a Java 8 Stream.

Maggiore è la produttività, migliori le prestazioni


1
Grazie, ma la domanda era molto specifica nel contesto dell'API Stream
RichK,

Sì, sono d'accordo, ho già menzionato "Mentre la risposta più votata è assolutamente la migliore risposta rispetto a Java 8". Un problema può essere risolto in diversi modi e sto cercando di evidenziare che il problema in questione può essere risolto semplicemente, piuttosto che pericolosamente con Java 8 Streams, dove il pericolo è il degrado delle prestazioni. :)
Abhinav Ganguly,

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

Se si desidera l'elenco delle persone che segue sarebbe il modo semplice

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

Inoltre, se si desidera trovare un elenco di nomi distinto o univoco , non Person , è possibile eseguire anche utilizzando due metodi seguenti.

Metodo 1: utilizzo distinct

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

Metodo 2: utilizzo HashSet

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

2
Questo produce un elenco di nomi, non di Persons.
Hulk,

1
Questo e 'esattamente quello che stavo cercando. Avevo bisogno di un metodo a linea singola per eliminare i duplicati mentre trasformavo una raccolta in un altro. Grazie.
Raj

-3

Il codice più semplice che puoi scrivere:

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

12
Questo otterrà un elenco distinto di nomi, non persone per nome
RichK
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.