Java 8 Mülkiyete Göre Farklı


456

Java 8'de Stream, her nesnenin bir özelliğinin farklılığını kontrol ederek API kullanarak bir koleksiyonu nasıl filtreleyebilirim ?

Örneğin bir Personnesne listem var ve aynı ada sahip insanları kaldırmak istiyorum,

persons.stream().distinct();

Bir Personnesne için varsayılan eşitlik denetimini kullanacak , bu yüzden şöyle bir şeye ihtiyacım var,

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

Ne yazık ki distinct()yöntemin böyle bir aşırı yüklemesi yoktur. PersonSınıf içindeki eşitlik kontrolünü değiştirmeden bunu özlü bir şekilde yapmak mümkün müdür?

Yanıtlar:


557

Durum bilgisi olan bir filtredistinct olmayı düşünün . Daha önce gördükleri hakkında durumu koruyan ve verilen öğenin ilk kez görülüp görülmediğini döndüren bir yüklem döndüren bir işlev şunlardır:

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

Sonra yazabilirsiniz:

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

Akış sipariş edilirse ve paralel olarak çalıştırılırsa , bunun ilk kopya yerine kopyalar arasından isteğe bağlı bir öğe koruyacağını unutmayın distinct().

(Bu aslında bu soruya verdiğim cevapla aynı : Keyfi anahtarda Java Lambda Stream Distinct ()? )


27
Sanırım daha iyi uyumluluk için argüman olmalı Function<? super T, ?>, değil Function<? super T, Object>. Ayrıca, sıralı paralel akış için bu çözeltinin hangi nesnenin çıkarılacağını (normalden farklı olarak distinct()) garanti etmediğine dikkat edilmelidir . Ayrıca sıralı akışlar için CHM kullanımında ek yük vardır (bu, nosid çözeltisinde yoktur). Son olarak bu çözüm filter, JavaDoc'da belirtildiği gibi yüklemin vatansız olması gereken yöntem sözleşmesini ihlal etmektedir . Yine de iptal edildi.
Tagir Valeev

3
@java_newbie Döndürülen Predicate örneğinin distinctByKey, bunun paralel bir akış içinde kullanılıp kullanılmadığı hakkında hiçbir fikri yoktur. Paralel olarak kullanılması durumunda CHM kullanır, ancak bu, Tagir Valeev'in yukarıda belirtildiği gibi sıralı durumda ek yük getirir.
Stuart Marks

5
@holandaGo Döndürülen Predicate örneğini kaydedip yeniden kullanırsanız başarısız olur distinctByKey. Ancak distinctByKeyher seferinde çağırırsanız çalışır , böylece her seferinde yeni bir Predicate örneği oluşturur.
Stuart Marks

3
@Chinmay hayır, olmamalı. Eğer kullanırsanız .filter(distinctByKey(...)). Yöntemi bir kez yürütür ve yüklemi döndürür. Dolayısıyla, bir akış içinde düzgün bir şekilde kullanırsanız, harita zaten yeniden kullanılmaktadır. Haritayı statik hale getirirseniz, harita tüm kullanımlar için paylaşılır. Dolayısıyla, bunu kullanan iki akışınız varsa distinctByKey(), her ikisi de aynı haritayı kullanır;
g00glen00b

3
Bu sooo akıllı ve tamamen açık değil. Genellikle bu durumsal lamda ve altta yatan CallSitebağlanacak olan get$Lambdayöntemin - yeni bir örneğini döneceğini Predicateher zaman, ancak bu örnekler aynı paylaşacak mapve functionbildiğim kadarıyla anladığım kadarıyla. Çok hoş!
Eugene

152

Alternatif olarak, kişileri anahtar olarak kullanarak bir haritaya yerleştirmek olabilir:

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

Yinelenen bir isim olması durumunda, tutulan Kişinin ilk olarak katılacağını unutmayın.


23
@skiwi: Sizce distinct()bu ek yük olmadan uygulama yapmanın bir yolu var mı? Herhangi bir uygulama, gördüğü tüm farklı değerleri gerçekten hatırlamadan daha önce bir nesne görüp görmediğini nasıl bilebilir? Yani yükü toMapve distinctbüyük olasılıkla aynı.
Holger

1
@Holger Ben havai distinct()kendisi yaratıyor abou sanmıyordu gibi orada yanlış olabilir .
skiwi

2
Ve tabii ki listenin orijinal sırasını
Philipp

10
@Philipp:persons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
Holger

1
@DanielEarwicker bu soru "mülkiyete göre farklı" hakkındadır. Akışın avantajlarından yararlanabilmek için aynı özelliğe göre sıralanmasını gerektirir . İlk olarak, OP hiçbir zaman akışın hiç sıralanmadığını belirtmedi. İkincisi, akışlar belirli bir özelliğe göre sıralanıp sıralanmadığını algılayamaz . Üçüncüsü, önerilerinizi yapmak için gerçek bir "mülke göre farklı" akış işlemi yoktur. Dördüncü olarak, pratikte, bu tür bir sıralanmış akışı elde etmenin sadece iki yolu vardır. TreeSetZaten zaten farklı sortedolan ve akışta tüm öğeleri arabelleğe alan sıralı bir kaynak ( ) .
Holger

101

Kişi nesnelerini, yalnızca kişilerin adlarını karşılaştıran başka bir sınıfa sarabilirsiniz. Daha sonra, bir kişi akışını yeniden almak için sarılmış nesneleri açarsınız. Akış işlemleri aşağıdaki gibi görünebilir:

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

Sınıf Wrapperaşağıdaki gibi görünebilir:

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 Gerçekten değil ... herhangi bir not var ve asıl mesele performans değil, mevcut API'ya adaptasyon.
Marko Topolnik

6
com.google.common.base.Equivalence.wrap (S) ve com.google.common.base.Equivalence.Wrapper.get () de yardımcı olabilir.
bjmi

Sarıcı sınıfını bir anahtar çıkarma işlevi ile genel ve parametreler haline getirebilirsiniz.
Lii

equalsYöntem basitleştiirlebilirreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Holger

55

Kullanarak başka bir çözüm Set. İdeal çözüm olmayabilir, ama işe yarıyor

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

Veya orijinal listeyi değiştirebilirseniz, removeIf yöntemini kullanabilirsiniz

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

2
Herhangi bir üçüncü taraf kitaplığı kullanmıyorsanız en iyi cevap budur!
Manoj Shrestha

5
bu küme belirtilen öğeyi içermiyorsa Set.add öğesinin true döndürdüğünü gösteren bir fikir. +1
Luvie

Bu yöntemin iş parçacığı için güvenli olmadığı için paralel akış işleme için çalışmadığına inanıyorum.
LoBo

@LoBo Muhtemelen hayır. Bu sadece basit durumlar için işe yarayacak bir fikir. Kullanıcılar iplik güvenliği / paralellik için genişletebilir.
Mart'ta Santhosh

İlginç bir yaklaşım, ancak başka bir koleksiyondaki (kişiler) bir akışı filtrelerken harici bir koleksiyonu (seti) değiştirmek için bir anti-desen gibi görünüyor ...
Justin Rowe

31

Özel bir karşılaştırıcıya sahip bir TreeSet kullanarak daha basit bir yaklaşım var.

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

4
Cevabınızın benzersizliğe değil sıralamaya doğru yardımcı olduğunu düşünüyorum. Ancak bu nasıl yapılacağına dair düşüncelerimi belirlememe yardımcı oldu. Buraya bakın: stackoverflow.com/questions/1019854/…
janagn

Buradaki öğeleri sıralamak için fiyat ödeyeceğinizi ve kopyaları bulmak veya hatta kopyaları kaldırmak için sıralamaya ihtiyacımız olmadığını unutmayın.
pisaruk

12
Comparator.comparing (Kişi :: getName)
Jean-François Savard

24

Ayrıca RxJava (çok güçlü reaktif uzatma kütüphanesi) kullanabiliriz

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

veya

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

Rx harika, ama bu kötü bir cevap. Observableitme tabanlıdır, oysa Streamçekme tabanlıdır. stackoverflow.com/questions/30216979/…
sdgfsdh

4
soru, mutlaka akış kullanmayan bir java8 çözümü istemektedir. Benim cevap java8 akışı api rx api daha az güçlü olduğunu göstermektedir
frhack

1
Reaktör kullanarak , olacakFlux.fromIterable(persons).distinct(p -> p.getName())
Ritesh

Soru kelimenin tam anlamıyla Stream"mutlaka akış kullanmayan" değil, "API kullanma" diyor . Bununla birlikte, akışın farklı değerlere filtrelenmesi XY sorununa mükemmel bir çözümdür.
M Justin

12

groupingByToplayıcıyı kullanabilirsiniz :

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

Başka bir akışa sahip olmak istiyorsanız bunu kullanabilirsiniz:

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

11

Bu distinct(HashingStrategy)yöntemi Eclipse Collections'ta kullanabilirsiniz .

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

personsEclipse Collections arabirimini uygulamak için refactor yapabilirsiniz, yöntemi doğrudan listeden çağırabilirsiniz.

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

HashingStrategy , eşittir ve hashcode özel uygulamalarını tanımlamanızı sağlayan bir strateji arabirimidir.

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

Not: Eclipse Collections için bir komisyoncuyum.


Bu çözümü daha da basitleştirebilen Eclipse Collections 9.0'a differenttBy yöntemi eklendi. medium.com/@donraab/…
Donald Raab

10

Mümkünse Vavr kullanmanızı öneririm . Bu kütüphane ile aşağıdakileri yapabilirsiniz:

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

Eskiden "javaslang" kütüphanesi olarak biliniyordu.
user11153

9

StreamEx kütüphanesini kullanabilirsiniz :

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

Ne yazık ki, aksi halde harika StreamEx kütüphanesinin bu yöntemi zayıf bir şekilde tasarlanmıştır - eşittir yerine nesne eşitliğini karşılaştırır. Bu, Stringdize stajyerliği sayesinde s için işe yarayabilir , ancak işe yaramayabilir .
Torque

7

Stuart Marks'ın cevabını genişleterek, bu daha kısa bir şekilde ve eşzamanlı bir harita olmadan yapılabilir (paralel akışlara ihtiyacınız yoksa):

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

Sonra ara:

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

2
Bu, akışın paralel olabileceği dikkate alınmaz.
brunnsbe

Yorum için teşekkürler, cevabımı güncelledim. Paralel bir akışa ihtiyacınız yoksa, eşzamanlı haritalar kullanmamanız size çok daha iyi performans sağlar.
Wojciech Górski

Collections.synchronizedSet(new HashSet<>())Bunun yerine bir kod oluşturduysanız kodunuz muhtemelen paralel koleksiyonlar için kullanılabilir. Ama muhtemelen a'dan daha yavaş olurdu ConcurrentHashMap.
Lii

7

Saeed Zarinfam'ın kullandığı benzer yaklaşım ancak daha fazla Java 8 stili :)

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

1
Harita çizgisini onunla değiştirirdim flatMap(plans -> plans.stream().findFirst().stream())İsteğe bağlı olarak kullanılmasını önler
Andrew Sneck

Belki de sorun yoktur: flatMap (plans -> plans.stream (). Limit (1))
Rrr

6

Genel bir versiyon yaptım:

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

Bir örnek:

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

Buna yaklaşımım, aynı özelliğe sahip tüm nesneleri bir arada gruplamak, sonra grupları 1 boyuta kısaltmak ve sonunda bunları bir 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

Farklı nesneler listesi aşağıdakiler kullanılarak bulunabilir:

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

2

Bunu uygulamanın en kolay yolu, Comparatorbir öğenin özelliği kullanılarak oluşturulabilen isteğe bağlı bir özellik sağladığından sıralama özelliğine atlamaktır. Daha sonra Predicate, sıralı bir akış için tüm eşit öğelerin bitişik olduğu gerçeğini kullanan bir durum kullanarak yapılabilecek kopyaları filtrelemeniz gerekir :

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

Tabii ki, bir durum Predicateiş parçacığı için güvenli değildir, ancak eğer ihtiyacınız varsa, bu mantığı bir içine taşıyabilir Collectorve akışınızı kullanarak iş parçacığının güvenliğine bakabilirsiniz Collector. Bu, sorunuzda bize söylemediğiniz farklı unsurların akışı ile ne yapmak istediğinize bağlıdır.


1

@ Josketres'in cevabını temel alarak genel bir yardımcı program yöntemi oluşturdum:

Bir Koleksiyoncu oluşturarak bunu daha fazla Java 8 dostu yapabilirsiniz .

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

Belki biri için faydalı olacaktır. Biraz başka bir gereksinim daha vardı. A3. taraftaki nesnelerin listesine sahip olmak, aynı A.balana sahip olanların tümünü kaldırmak A.id( listede Aaynı A.idolan birden fazla nesne ). Tagir Valeev'in Stream partition cevabı bana özel olanı kullanmam için ilham verdi . Basit gerisini halleder.CollectorMap<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 tuşa dayanarak listeden farklı öğeler alabileceğim bir durumum vardı. İki tuşa dayalı farklı bir tuş istiyorsanız veya birleşik tuşa basabilirsiniz, bunu deneyin

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

Benim durumumda, önceki öğenin ne olduğunu kontrol etmem gerekiyordu. Daha sonra , önceki öğenin geçerli öğeden farklı olup olmadığını denetlediğim, bu durumda tuttuğum durum bilgisi olan bir Predicate oluşturdum .

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

Bu listedeki çözümüm:

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

Benim durumumda farklı değerler bulmak ve onların Listeye koymak istiyorum.


0

En yüksek oylanan cevap Java 8 wrt kesinlikle en iyi cevap olsa da, aynı zamanda performans açısından kesinlikle en kötü yanıtı. Gerçekten kötü bir düşük performanslı uygulama istiyorsanız, devam edin ve kullanın. Benzersiz bir Kişi Adları grubunun çıkarılması için basit bir gereklilik sadece "Her biri için" ve "Set" ile sağlanacaktır. Liste 10'dan büyükse işler daha da kötüleşir.

Aşağıdaki gibi 20 Nesne koleksiyonunuz olduğunu düşünün:

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

İtiraz ettiğiniz yer SimpleEventşöyle:

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

Ve test etmek için, böyle JMH kodunuz var , (Lütfen dikkat, kabul edilen cevapta belirtilen aynı farklı imi kullanıyorum ):

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

Sonra Benchmark sonuçları şöyle olacaktır:

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

Gördüğünüz gibi, basit bir For-Each , iş hacminde 3 kat daha iyi ve Java 8 Stream ile karşılaştırıldığında hata puanında daha az.

Daha yüksek verim, daha iyi performans


1
Teşekkürler, ancak soru özellikle Akış API'sı bağlamındaydı
RichK

Evet, katılıyorum, ben zaten bahsetmiştim "en yüksek upvoted cevap kesinlikle en iyi cevap wrt Java 8". Bir sorun n farklı yolla çözülebilir ve burada eldeki sorunun, tehlikenin performans düşüşü olduğu Java 8 Akışları ile tehlikeli olarak değil, basit bir şekilde çözülebileceğini vurgulamaya çalışıyorum. :)
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

Kişi Listesini istiyorsanız aşağıdaki basit yol olacaktır

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

Ayrıca, Kişi değil , farklı veya benzersiz bir ad listesi bulmak istiyorsanız , sen de iki yöntemin aşağıdaki kullanarak yapabilirsiniz.

Yöntem 1: kullanma distinct

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

Yöntem 2: kullanma HashSet

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

2
Bu, Persons değil, bir ad listesi oluşturur .
Hulk

1
Tam da aradığım şey buydu. Bir koleksiyonu birbirine dönüştürürken yinelenenleri ortadan kaldırmak için tek bir satır yöntemine ihtiyacım vardı. Teşekkürler.
Raj

-3

Yazabileceğiniz en basit kod:

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

12
Kişiler adına göre değil, adların ayrı bir listesini alırsınız
RichK
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.