Java 8 Berbeda dengan properti

456

Di Java 8, bagaimana saya bisa memfilter koleksi menggunakan StreamAPI dengan memeriksa perbedaan properti setiap objek?

Misalnya saya punya daftar Personobjek dan saya ingin menghapus orang dengan nama yang sama,

persons.stream().distinct();

Akan menggunakan pemeriksaan kesetaraan default untuk Personobjek, jadi saya perlu sesuatu seperti,

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

Sayangnya distinct()metode ini tidak memiliki kelebihan seperti itu. Tanpa memodifikasi pemeriksaan kesetaraan di dalam Personkelas, mungkinkah melakukan ini dengan ringkas?

RichK
sumber

Jawaban:

557

Pertimbangkan distinctuntuk menjadi filter stateful . Berikut adalah fungsi yang mengembalikan predikat yang mempertahankan status tentang apa yang dilihat sebelumnya, dan yang mengembalikan apakah elemen yang diberikan terlihat untuk pertama kalinya:

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

Maka Anda dapat menulis:

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

Perhatikan bahwa jika aliran diperintahkan dan dijalankan secara paralel, ini akan mempertahankan elemen sewenang - wenang dari antara duplikat, bukan yang pertama, seperti distinct()halnya.

(Ini pada dasarnya sama dengan jawaban saya untuk pertanyaan ini: Java Lambda Stream Distinct () pada kunci sewenang-wenang? )

Stuart Marks
sumber
27
Saya kira untuk kompatibilitas yang lebih baik argumennya seharusnya Function<? super T, ?>, bukan Function<? super T, Object>. Juga harus dicatat bahwa untuk aliran paralel yang dipesan solusi ini tidak menjamin objek mana yang akan diekstraksi (tidak seperti normal distinct()). Juga untuk aliran berurutan ada overhead tambahan dalam menggunakan CHM (yang tidak ada dalam solusi @nosid). Akhirnya solusi ini melanggar kontrak filtermetode yang predikatnya harus stateless seperti yang dinyatakan dalam JavaDoc. Namun demikian, diputuskan.
Tagir Valeev
3
@java_newbie Contoh Predicate yang dikembalikan oleh distinctByKeytidak tahu apakah itu digunakan dalam aliran paralel. Ini menggunakan CHM jika sedang digunakan secara paralel, meskipun ini menambahkan overhead dalam kasus berurutan seperti yang dicatat Tagir Valeev di atas.
Stuart Marks
5
@holandaGo Akan gagal jika Anda menyimpan dan menggunakan kembali contoh Predikat yang dikembalikan oleh distinctByKey. Tapi itu berfungsi jika Anda menelepon distinctByKeysetiap waktu, sehingga menciptakan contoh Predikat baru setiap kali.
Stuart Marks
3
@Chinmay tidak, seharusnya tidak. Jika Anda menggunakan .filter(distinctByKey(...)). Ini akan mengeksekusi metode sekali dan mengembalikan predikat. Jadi pada dasarnya peta sudah digunakan kembali jika Anda menggunakannya dengan benar dalam aliran. Jika Anda membuat peta statis, peta akan dibagikan untuk semua penggunaan. Jadi jika Anda memiliki dua aliran menggunakan ini distinctByKey(), keduanya akan menggunakan peta yang sama, yang bukan yang Anda inginkan.
g00glen00b
3
Ini sangat pintar dan sama sekali tidak jelas. Secara umum ini adalah lambda stateful dan yang mendasarinya CallSiteakan dikaitkan dengan get$Lambdametode - yang akan mengembalikan contoh baru Predicatesepanjang waktu, tetapi contoh-contoh itu akan berbagi sama mapdan functionsejauh yang saya mengerti. Sangat bagus!
Eugene
152

Alternatifnya adalah dengan menempatkan orang-orang di peta menggunakan nama sebagai kunci:

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

Perhatikan bahwa Orang yang disimpan, dalam kasus duplikat nama, akan menjadi orang pertama yang dikonfigurasikan.

wha'eve '
sumber
23
@skiwi: apakah menurut Anda ada cara untuk menerapkan distinct()tanpa overhead itu? Bagaimana setiap implementasi tahu jika ia telah melihat objek sebelumnya tanpa benar-benar mengingat semua nilai berbeda yang telah dilihatnya? Jadi overhead toMapdan distinctsangat mungkin sama.
Holger
1
@ Holger Saya mungkin salah di sana karena saya tidak berpikir tentang overhead distinct()itu sendiri menciptakan.
skiwi
2
Dan jelas itu mengacaukan urutan asli daftar
Philipp
10
@ Pilip: bisa diperbaiki dengan mengubah kepersons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
Holger
1
@DanielEarwicker pertanyaan ini adalah tentang "berbeda dengan properti". Itu akan membutuhkan aliran untuk diurutkan berdasarkan properti yang sama , untuk dapat memanfaatkannya. Pertama, OP tidak pernah menyatakan bahwa aliran diurutkan sama sekali. Kedua, stream tidak dapat mendeteksi apakah mereka diurutkan berdasarkan properti tertentu . Ketiga, tidak ada operasi aliran "berbeda berdasarkan properti" asli untuk melakukan apa yang Anda sarankan. Keempat, dalam praktiknya, hanya ada dua cara untuk mendapatkan aliran yang diurutkan. Sumber diurutkan ( TreeSet) yang sudah berbeda pula atau sortedpada aliran yang juga menyangga semua elemen.
Holger
101

Anda dapat membungkus objek orang ke dalam kelas lain, yang hanya membandingkan nama orang tersebut. Setelah itu, Anda membuka bungkusan benda yang dibungkus untuk membuat orang streaming lagi. Operasi aliran mungkin terlihat sebagai berikut:

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

Kelas Wrappermungkin terlihat sebagai berikut:

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();
    }
}
tidak ada
sumber
13
Ini disebut transformasi Schwartzian
Stuart Caie
5
@StuartCaie Tidak benar-benar ... tidak ada memoisasi, dan intinya bukan kinerja, tetapi adaptasi dengan API yang ada.
Marko Topolnik
6
com.google.common.base.Equivalence.wrap (S) dan com.google.common.base.Equivalence.Wrapper.get () juga bisa membantu.
bjmi
Anda bisa membuat kelas pembungkus generik dan dipisah-pisahkan dengan fungsi ekstraksi kunci.
Lii
The equalsMetode dapat disederhanakanreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Holger
55

Solusi lain, menggunakan Set. Mungkin bukan solusi yang ideal, tetapi berhasil

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

Atau jika Anda dapat mengubah daftar asli, Anda dapat menggunakan metode removeIf

persons.removeIf(p -> !set.add(p.getName()));
Santhosh
sumber
2
Ini adalah jawaban terbaik jika Anda tidak menggunakan perpustakaan pihak ketiga!
Manoj Shrestha
5
menggunakan ide genious yang Set.add mengembalikan true jika set ini belum mengandung elemen yang ditentukan. +1
Luvie
Saya percaya metode ini tidak berfungsi untuk pemrosesan aliran paralel, karena tidak aman untuk thread.
LoBo
@ LoBo Mungkin tidak. Ini hanya sebuah ide, yang akan bekerja untuk kasus-kasus sederhana. Pengguna dapat memperluasnya untuk keselamatan / paralelisme benang.
Santhosh
Pendekatan yang menarik, tetapi terlihat seperti anti-pola untuk memodifikasi koleksi eksternal (set) sementara menyaring aliran pada koleksi (orang) lain ...
Justin Rowe
31

Ada pendekatan yang lebih sederhana menggunakan TreeSet dengan pembanding kustom.

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName())) 
));
Josephketres
sumber
4
Saya pikir jawaban Anda membantu pemesanan dan bukan keunikan. Namun itu membantu saya mengatur pikiran saya tentang bagaimana melakukannya. Periksa di sini: stackoverflow.com/questions/1019854/...
janagn
Ingatlah bahwa Anda akan membayar harga untuk menyortir elemen di sini dan kami tidak perlu menyortir untuk menemukan duplikat atau bahkan menghapus duplikat.
pisaruk
12
Comparator.comparing (Orang :: getName)
Jean-François Savard
24

Kita juga dapat menggunakan RxJava ( pustaka ekstensi reaktif yang sangat kuat )

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

atau

Observable.from(persons).distinct(p -> p.getName())
frhack
sumber
Rx memang luar biasa, tapi ini jawaban yang buruk. Observableberbasis push sedangkan berbasis Streampull. stackoverflow.com/questions/30216979/…
sdgfsdh
4
pertanyaannya meminta solusi java8 belum tentu menggunakan aliran. Jawaban saya menunjukkan bahwa java8 stream api kurang powefull daripada rx api
frhack
1
Menggunakan reaktor , itu akan menjadiFlux.fromIterable(persons).distinct(p -> p.getName())
Ritesh
Pertanyaannya secara harfiah mengatakan "menggunakan StreamAPI", bukan "tidak harus menggunakan aliran". Yang mengatakan, ini adalah solusi bagus untuk masalah XY menyaring aliran ke nilai yang berbeda.
M. Justin
12

Anda dapat menggunakan groupingBykolektor:

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

Jika Anda ingin memiliki aliran lain, Anda dapat menggunakan ini:

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

Anda dapat menggunakan distinct(HashingStrategy)metode ini di Eclipse Collections .

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

Jika Anda bisa menolak personsuntuk mengimplementasikan antarmuka Eclipse Collections, Anda dapat memanggil metode secara langsung pada daftar.

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

HashingStrategy hanyalah antarmuka strategi yang memungkinkan Anda untuk menentukan implementasi kustom dengan equals dan hashcode.

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

Catatan: Saya pengendara untuk Eclipse Collections.

Craig P. Motlin
sumber
Metode differBy ditambahkan dalam Eclipse Collections 9.0 yang selanjutnya dapat menyederhanakan solusi ini. medium.com/@donraab/…
Donald Raab
10

Saya sarankan menggunakan Vavr , jika Anda bisa. Dengan perpustakaan ini Anda dapat melakukan hal berikut:

io.vavr.collection.List.ofAll(persons)
                       .distinctBy(Person::getName)
                       .toJavaSet() // or any another Java 8 Collection
Mateusz Rasiński
sumber
Sebelumnya dikenal sebagai perpustakaan "javaslang".
user11153
9

Anda dapat menggunakan perpustakaan StreamEx :

StreamEx.of(persons)
        .distinct(Person::getName)
        .toList()
Sllouyssgort
sumber
Sayangnya, metode pustaka StreamEx yang dinyatakan luar biasa itu didesain dengan buruk - ia membandingkan kesetaraan objek alih-alih menggunakan yang sama. Ini mungkin berhasil karena Stringterima kasih kepada string interning, tetapi juga mungkin tidak.
Torsi
7

Memperluas jawaban Stuart Marks, ini dapat dilakukan dengan cara yang lebih pendek dan tanpa peta bersamaan (jika Anda tidak membutuhkan aliran paralel):

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

Lalu hubungi:

persons.stream().filter(distinctByKey(p -> p.getName());
Wojciech Górski
sumber
2
Yang ini tidak mempertimbangkan bahwa aliran mungkin paralel.
brunnsbe
Terima kasih atas komentarnya, saya telah memperbarui jawaban saya. Jika Anda tidak membutuhkan aliran paralel, tidak menggunakan peta konkuren memberi Anda kinerja yang jauh lebih baik.
Wojciech Górski
Kode Anda mungkin akan berfungsi untuk koleksi paralel jika Anda membuatnya Collections.synchronizedSet(new HashSet<>()). Tetapi mungkin akan lebih lambat daripada dengan ConcurrentHashMap.
Lii
7

Pendekatan serupa yang digunakan Saeed Zarinfam tetapi lebih gaya Java 8 :)

persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream()
 .map(plans -> plans.stream().findFirst().get())
 .collect(toList());
Alex
sumber
1
Saya akan mengganti garis peta dengan flatMap(plans -> plans.stream().findFirst().stream())itu menghindari penggunaan get on Optional
Andrew Sneck
Mungkin ini juga ok: flatMap (plan -> plan.stream (). Limit (1))
Rrr
6

Saya membuat versi generik:

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

Contoh:

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())
Guillaume Cornet
sumber
6
Set<YourPropertyType> set = new HashSet<>();
list
        .stream()
        .filter(it -> set.add(it.getYourProperty()))
        .forEach(it -> ...);
Andrew Novitskyi
sumber
2
Jawaban yang baik memiliki penjelasan yang lebih baik. Bagaimana cara saya menulis jawaban yang baik?
Narendra Jadhav
5

Pendekatan saya untuk ini adalah untuk mengelompokkan semua objek dengan properti yang sama bersama, lalu potong pendek grup menjadi ukuran 1 dan akhirnya kumpulkan sebagai 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
sumber
3

Daftar objek yang berbeda dapat ditemukan menggunakan:

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

Cara termudah untuk mengimplementasikan ini adalah dengan melompat pada fitur pengurutan karena sudah menyediakan opsi Comparatoryang dapat dibuat menggunakan properti elemen. Maka Anda harus memfilter duplikat yang dapat dilakukan menggunakan statefull Predicateyang menggunakan fakta bahwa untuk aliran yang diurutkan semua elemen yang sama berbatasan:

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

Tentu saja, statefull Predicatebukan thread-safe, namun jika itu kebutuhan Anda, Anda dapat memindahkan logika ini ke Collectordan membiarkan aliran menjaga keamanan thread saat menggunakan Anda Collector. Ini tergantung pada apa yang ingin Anda lakukan dengan aliran elemen berbeda yang tidak Anda beri tahu kami dalam pertanyaan Anda.

Holger
sumber
1

Berdasarkan jawaban @ josketres, saya membuat metode utilitas umum:

Anda bisa menjadikan Java 8 ini lebih ramah dengan membuat Kolektor .

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
sumber
1

Mungkin akan bermanfaat bagi seseorang. Saya punya sedikit persyaratan lain. Memiliki daftar objek Adari pihak ke-3 menghapus semua yang memiliki A.bbidang yang sama untuk yang sama A.id(beberapa Aobjek dengan A.iddaftar yang sama ). Aliran partisi jawaban oleh Tagir Valeev menginspirasi saya untuk menggunakan custom Collectoryang kembali Map<A.id, List<A>>. Sederhana flatMapakan melakukan sisanya.

 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
sumber
1

Saya punya situasi, di mana saya seharusnya mendapatkan elemen yang berbeda dari daftar berdasarkan 2 kunci. Jika Anda ingin berbeda berdasarkan pada dua tombol atau mungkin tombol komposit, coba ini

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 menanduk
sumber
0

Dalam kasus saya, saya perlu mengontrol apa elemen sebelumnya. Saya kemudian membuat Predikat stateful di mana saya mengontrol jika elemen sebelumnya berbeda dari elemen saat ini, dalam hal itu saya menyimpannya.

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
sumber
0

Solusi saya dalam daftar ini:

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

Dalam situasi saya, saya ingin menemukan nilai yang berbeda dan memasukkannya ke dalam Daftar.

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

Sementara jawaban tertinggi yang dipilih adalah jawaban terbaik untuk Java 8, namun pada saat yang sama benar-benar terburuk dalam hal kinerja. Jika Anda benar-benar menginginkan aplikasi berkinerja rendah yang buruk, silakan gunakan. Persyaratan sederhana untuk mengekstraksi seperangkat Nama Person yang unik harus dicapai hanya dengan "Untuk Setiap" dan "Set". Hal-hal menjadi lebih buruk jika daftar di atas ukuran 10.

Pertimbangkan Anda memiliki koleksi 20 Objek, seperti ini:

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

Di mana Anda keberatan SimpleEventterlihat seperti ini:

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

Dan untuk menguji, Anda memiliki JMH kode seperti ini, (Harap dicatat, im menggunakan yang sama distinctByKey Predikat disebutkan dalam jawaban diterima):

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

Maka Anda akan mendapatkan hasil Benchmark seperti ini:

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

Dan seperti yang Anda lihat, For-Each sederhana adalah 3 kali lebih baik dalam throughput dan skor kesalahan kurang dibandingkan dengan Java 8 Stream.

Semakin tinggi throughput, semakin baik kinerjanya

Abhinav Ganguly
sumber
1
Terima kasih, tetapi pertanyaannya sangat spesifik dalam konteks Stream API
RichK
Ya, saya setuju, saya sudah menyebutkan "Sementara jawaban tertinggi yang dipilih adalah jawaban terbaik untuk Java 8". Masalah dapat diselesaikan dengan berbagai cara, dan saya mencoba untuk menyoroti di sini bahwa masalah yang ada dapat diselesaikan secara sederhana, dan bukannya berbahaya dengan Java 8 Streams, di mana bahayanya adalah penurunan kinerja. :)
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
sumber
-2

Jika Anda ingin Daftar Orang berikut ini akan menjadi cara sederhana

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

Selain itu, jika Anda ingin menemukan daftar nama yang berbeda atau unik , bukan Orang , Anda dapat menggunakan dua metode berikut juga.

Metode 1: menggunakan distinct

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

Metode 2: menggunakan HashSet

Set<E> set = new HashSet<>();
set.addAll(person.stream().map(x->x.getName()).collect(Collectors.toList()));
Abdur Rahman
sumber
2
Ini menghasilkan daftar nama, bukan Persons.
Hulk
1
Inilah yang saya cari. Saya membutuhkan metode garis tunggal untuk menghilangkan duplikat saat mengubah koleksi ke yang lain. Terima kasih.
Raj
-3

Kode paling sederhana yang dapat Anda tulis:

    persons.stream().map(x-> x.getName()).distinct().collect(Collectors.toList());
2Big2BeSmall
sumber
12
Itu akan mendapatkan daftar nama yang berbeda, bukan Orang dengan namanya
RichK