Java 8 distinto por propiedad

456

En Java 8, ¿cómo puedo filtrar una colección usando la StreamAPI comprobando la distinción de una propiedad de cada objeto?

Por ejemplo, tengo una lista de Personobjetos y quiero eliminar personas con el mismo nombre,

persons.stream().distinct();

Usaré la verificación de igualdad predeterminada para un Personobjeto, así que necesito algo como,

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

Lamentablemente, el distinct()método no tiene tal sobrecarga. Sin modificar la verificación de igualdad dentro de la Personclase, ¿es posible hacer esto sucintamente?

RichK
fuente

Respuestas:

557

Considere distinctser un filtro con estado . Aquí hay una función que devuelve un predicado que mantiene el estado sobre lo que se vio anteriormente, y que devuelve si el elemento dado se vio por primera vez:

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

Entonces puedes escribir:

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

Tenga en cuenta que si la secuencia se ordena y se ejecuta en paralelo, esto preservará un elemento arbitrario entre los duplicados, en lugar del primero, como lo distinct()hace.

(Esto es esencialmente lo mismo que mi respuesta a esta pregunta: Java Lambda Stream Distinct () en clave arbitraria? )

Stuart Marks
fuente
27
Supongo que para una mejor compatibilidad, el argumento debería ser Function<? super T, ?>, no Function<? super T, Object>. También debe tenerse en cuenta que para la secuencia paralela ordenada, esta solución no garantiza qué objeto se extraerá (a diferencia de lo normal distinct()). También para las secuencias secuenciales hay una sobrecarga adicional en el uso de CHM (que está ausente en la solución @nosid). Finalmente, esta solución viola el contrato del filtermétodo cuyo predicado debe ser sin estado como se indica en JavaDoc. Sin embargo, votó positivamente.
Tagir Valeev
3
@java_newbie La instancia de Predicate devuelta por distinctByKeyno tiene idea de si se está utilizando dentro de una secuencia paralela. Utiliza CHM en caso de que se use en paralelo, aunque esto agrega una sobrecarga en el caso secuencial como Tagir Valeev señaló anteriormente.
Stuart Marks el
55
@holandaGo Fallará si guarda y reutiliza la instancia de Predicate devuelta por distinctByKey. Pero funciona si llama distinctByKeycada vez, de modo que crea una nueva instancia de Predicate cada vez.
Stuart Marks
3
@Chinmay no, no debería. Si lo usas .filter(distinctByKey(...)). Ejecutará el método una vez y devolverá el predicado. Básicamente, el mapa ya se está reutilizando si lo usa correctamente dentro de una secuencia. Si hiciera el mapa estático, el mapa se compartiría para todos los usos. Entonces, si tiene dos transmisiones que usan esto distinctByKey(), ambas usarían el mismo mapa, que no es lo que desea.
g00glen00b
3
Esto es tan inteligente y completamente no obvio. En general, esta es una lambda con estado y el subyacente CallSiteestará vinculado al get$Lambdamétodo, que devolverá una nueva instancia de Predicatetodo el tiempo, pero esas instancias compartirán lo mismo mapy functionhasta donde yo entiendo. ¡Muy agradable!
Eugene
152

Una alternativa sería colocar a las personas en un mapa usando el nombre como clave:

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

Tenga en cuenta que la Persona que se mantiene, en caso de un nombre duplicado, será la primera encontrada.

lo que sea
fuente
23
@skiwi: ¿crees que hay una manera de implementar distinct()sin esa sobrecarga? ¿Cómo sabría cualquier implementación si ha visto un objeto antes sin recordar todos los valores distintos que ha visto? Por lo tanto, la sobrecarga de toMapy distinctes muy probable que sea la misma.
Holger
1
@Holger Puede que me haya equivocado allí ya que no había pensado en la sobrecarga que distinct()crea.
skiwi
2
Y obviamente estropea el orden original de la lista
Philipp
10
@Philipp: podría solucionarse cambiando apersons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
Holger
1
@DanielEarwicker esta pregunta es sobre "distinta por propiedad". Requeriría que la secuencia se ordene por la misma propiedad , para poder aprovecharla. Primero, el OP nunca declaró que la secuencia esté ordenada en absoluto. En segundo lugar, las transmisiones no pueden detectar si están ordenadas por una determinada propiedad . En tercer lugar, no existe una operación de flujo genuina "distinta por propiedad" para hacer lo que usted sugiere. En cuarto lugar, en la práctica, solo hay dos formas de obtener una secuencia tan ordenada. Una fuente ordenada ( TreeSet) que ya es distinta de todos modos o sorteden la secuencia que también almacena todos los elementos.
Holger
101

Puede ajustar los objetos de persona en otra clase, que solo compara los nombres de las personas. Luego, desenvuelve los objetos envueltos para que una persona vuelva a aparecer. Las operaciones de transmisión pueden tener el siguiente aspecto:

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

La clase Wrapperpodría verse de la siguiente manera:

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();
    }
}
nosid
fuente
13
Esto se llama la transformación de Schwartzian
Stuart Caie
55
@StuartCaie No realmente ... no hay memorización, y el punto no es el rendimiento, sino la adaptación a la API existente.
Marko Topolnik
66
com.google.common.base.Equivalence.wrap (S) y com.google.common.base.Equivalence.Wrapper.get () también podrían ayudar.
bjmi
Puede hacer que la clase de contenedor sea genérica y parametrizada mediante una función de extracción de clave.
Lii
El equalsmétodo se puede simplificar areturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Holger
55

Otra solución, usando Set. Puede que no sea la solución ideal, pero funciona

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

O si puede modificar la lista original, puede usar el método removeIf

persons.removeIf(p -> !set.add(p.getName()));
Santhosh
fuente
2
¡Esta es la mejor respuesta si no está utilizando bibliotecas de terceros!
Manoj Shrestha
55
usando la idea genial de que Set.add devuelve verdadero si este conjunto no contenía el elemento especificado. +1
Luvie
Creo que este método no funciona para el procesamiento de flujo paralelo, ya que no es seguro para subprocesos.
LoBo
@LoBo Probablemente no. Esto es solo una idea, que funcionará para casos simples. Los usuarios pueden extenderlo para seguridad de hilos / paralelismo.
Santhosh
Enfoque interesante, pero parece un antipatrón para modificar una colección externa (conjunto) mientras se filtra una secuencia en otra colección (personas) ...
Justin Rowe
31

Hay un enfoque más simple usando un TreeSet con un comparador personalizado.

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName())) 
));
josketres
fuente
44
Creo que su respuesta ayuda a ordenar y no a la singularidad. Sin embargo, me ayudó a establecer mis pensamientos sobre cómo hacerlo. Compruebe aquí: stackoverflow.com/questions/1019854/…
janagn
Tenga en cuenta que pagará el precio por ordenar los elementos aquí y no necesitamos ordenarlos para encontrar duplicados o incluso eliminar duplicados.
pisaruk
12
Comparator.comparing (Persona :: getName)
Jean-François Savard
24

También podemos usar RxJava ( biblioteca de extensión reactiva muy potente )

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

o

Observable.from(persons).distinct(p -> p.getName())
frhack
fuente
Rx es increíble, pero esta es una respuesta pobre. Observableestá basado en push mientras que Streamestá basado en pull. stackoverflow.com/questions/30216979/…
sdgfsdh
44
la pregunta pide una solución java8 que no necesariamente use stream. Mi respuesta muestra que la API de flujo de Java8 es menos potente que la API
RX
1
Usando el reactor , seráFlux.fromIterable(persons).distinct(p -> p.getName())
Ritesh
La pregunta literalmente dice "usando la StreamAPI", no "no necesariamente usando stream". Dicho esto, esta es una gran solución al problema XY de filtrar el flujo a valores distintos.
M. Justin
12

Puedes usar el groupingBycolector:

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

Si desea tener otra transmisión, puede usar esto:

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

Puede usar el distinct(HashingStrategy)método en Eclipse Collections .

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

Si puede refactorizar personspara implementar una interfaz Eclipse Collections, puede llamar al método directamente en la lista.

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

HashingStrategy es simplemente una interfaz de estrategia que le permite definir implementaciones personalizadas de equals y hashcode.

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

Nota: Soy un committer para Eclipse Collections.

Craig P. Motlin
fuente
El método distinctBy se agregó en Eclipse Collections 9.0 que puede simplificar aún más esta solución. medium.com/@donraab/…
Donald Raab
10

Recomiendo usar Vavr , si puedes. Con esta biblioteca puedes hacer lo siguiente:

io.vavr.collection.List.ofAll(persons)
                       .distinctBy(Person::getName)
                       .toJavaSet() // or any another Java 8 Collection
Mateusz Rasiński
fuente
Anteriormente conocida como biblioteca "javaslang".
user11153
9

Puede usar la biblioteca StreamEx :

StreamEx.of(persons)
        .distinct(Person::getName)
        .toList()
Sllouyssgort
fuente
Desafortunadamente, ese método de la impresionante biblioteca StreamEx está mal diseñado: compara la igualdad de objetos en lugar de usar iguales. Esto puede funcionar por Strings gracias al interning de cadenas, pero también puede no funcionar.
Par el
7

Ampliando la respuesta de Stuart Marks, esto se puede hacer de una manera más corta y sin un mapa concurrente (si no necesita flujos paralelos):

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

Luego llame:

persons.stream().filter(distinctByKey(p -> p.getName());
Wojciech Górski
fuente
2
Este no tiene en cuenta que la secuencia podría ser paralela.
brunnsbe
Gracias por el comentario, he actualizado mi respuesta. Si no necesita una transmisión paralela, no usar mapas concurrentes le brinda un rendimiento mucho mejor.
Wojciech Górski
Su código probablemente funcionaría para colecciones paralelas si creara un Collections.synchronizedSet(new HashSet<>())lugar. Pero probablemente sería más lento que con un ConcurrentHashMap.
Lii
7

Enfoque similar que utilizó Saeed Zarinfam pero más estilo Java 8 :)

persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream()
 .map(plans -> plans.stream().findFirst().get())
 .collect(toList());
Alex
fuente
1
Reemplazaría la línea del mapa con flatMap(plans -> plans.stream().findFirst().stream())él, evita el uso de get on Opcional
Andrew Sneck
Tal vez esto también esté bien: flatMap (planes -> planes.stream (). Limit (1))
Rrr
6

Hice una versión genérica:

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 ejemplo:

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())
Corneta Guillaume
fuente
6
Set<YourPropertyType> set = new HashSet<>();
list
        .stream()
        .filter(it -> set.add(it.getYourProperty()))
        .forEach(it -> ...);
Andrew Novitskyi
fuente
2
Una buena respuesta tiene una mejor explicación ¿Cómo escribo una buena respuesta?
Narendra Jadhav
5

Mi enfoque para esto es agrupar todos los objetos con la misma propiedad, luego acortar los grupos al tamaño de 1 y luego finalmente reunirlos como 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());
uneq95
fuente
3

La lista de objetos distintos se puede encontrar usando:

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

La forma más fácil de implementar esto es saltar a la función de clasificación, ya que proporciona una opción Comparatorque se puede crear utilizando la propiedad de un elemento. Luego, debe filtrar los duplicados que se pueden hacer usando un estado completo Predicateque utiliza el hecho de que para una secuencia ordenada todos los elementos iguales son adyacentes:

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

Por supuesto, un estado completo Predicateno es seguro para subprocesos, sin embargo, si esa es su necesidad, puede mover esta lógica a Collectory dejar que la transmisión se encargue de la seguridad de subprocesos cuando use su Collector. Esto depende de lo que quiera hacer con la secuencia de elementos distintos que no nos dijo en su pregunta.

Holger
fuente
1

Sobre la base de la respuesta de @ josketres, creé un método de utilidad genérico:

Puede hacer que esto sea más compatible con Java 8 creando un recopilador .

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;
    }
}
Garrett Smith
fuente
1

Quizás sea útil para alguien. Tenía un poco otro requisito. Tener una lista de objetos Ade terceros elimina todos los que tienen el mismo A.bcampo para el mismo A.id( Aobjeto múltiple con el mismo A.iden la lista). La respuesta de partición de flujo de Tagir Valeev me inspiró a usar la costumbre Collectorque regresa Map<A.id, List<A>>. Simple flatMaphará el 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)); }
Aliaksei Yatsau
fuente
1

Tuve una situación en la que se suponía que debía obtener elementos distintos de la lista basados ​​en 2 claves. Si desea una distinción basada en dos claves o una clave compuesta, intente esto

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());
Akanksha gore
fuente
0

En mi caso, necesitaba controlar cuál era el elemento anterior. Luego creé un predicado con estado donde controlaba si el elemento anterior era diferente del elemento actual, en ese caso lo guardé.

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

}
Flavio Oliva
fuente
0

Mi solución en este listado:

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

En mi situación, quiero encontrar valores distintos y ponerlos en la lista.

Евгений Трахимович
fuente
0

Si bien la respuesta más votada es absolutamente la mejor respuesta para Java 8, al mismo tiempo es absolutamente peor en términos de rendimiento. Si realmente desea una aplicación de bajo rendimiento, siga adelante y úsela. El simple requisito de extraer un conjunto único de nombres de personas se logrará mediante un mero "para cada uno" y un "conjunto". Las cosas empeoran aún más si la lista supera el tamaño de 10.

Considere que tiene una colección de 20 objetos, como este:

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

Donde objetas se SimpleEventve así:

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

Y para probar, tiene un código JMH como este (tenga en cuenta que estoy usando el mismo distintivo distinto de KeyKey mencionado en la respuesta aceptada):

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

Entonces tendrás resultados de referencia como este:

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

Y como puede ver, un For-Each simple es 3 veces mejor en rendimiento y menos en puntaje de error en comparación con Java 8 Stream.

Mayor rendimiento, mejor rendimiento

Abhinav Ganguly
fuente
1
Gracias, pero la pregunta fue muy específica en el contexto de la API Stream
RichK
Sí, estoy de acuerdo, ya he mencionado "Si bien la respuesta más votada es la mejor respuesta wrt Java 8". Un problema puede resolverse de diferentes maneras, y estoy tratando de resaltar aquí que el problema en cuestión puede resolverse de manera simple, en lugar de peligrosamente, con Java 8 Streams, donde el peligro es la degradación del rendimiento. :)
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]
Sourav Sharma
fuente
-2

Si desea hacer una lista de personas, la siguiente sería

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

Además, si desea encontrar una lista de nombres distinta o única , no Persona , también puede hacerlo utilizando los dos métodos siguientes.

Método 1: usando distinct

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

Método 2: usando HashSet

Set<E> set = new HashSet<>();
set.addAll(person.stream().map(x->x.getName()).collect(Collectors.toList()));
Abdur Rahman
fuente
2
Esto produce una lista de nombres, no Persons.
Hulk
1
Esto es exactamente lo que estaba buscando. Necesitaba un método de una sola línea para eliminar duplicados mientras transformaba una colección entre sí. Gracias.
Raj
-3

El código más simple que puedes escribir:

    persons.stream().map(x-> x.getName()).distinct().collect(Collectors.toList());
2Big2BeSmall
fuente
12
Sin embargo, eso obtendrá una lista distinta de nombres, no Personas por nombre
RichK