जावा 8 संपत्ति से भेद


456

जावा 8 में मैं Streamप्रत्येक वस्तु की संपत्ति की विशिष्टता की जांच करके एपीआई का उपयोग करके एक संग्रह को कैसे फ़िल्टर कर सकता हूं ?

उदाहरण के लिए मेरे पास Personऑब्जेक्ट की एक सूची है और मैं उसी नाम वाले लोगों को हटाना चाहता हूं,

persons.stream().distinct();

किसी Personवस्तु के लिए डिफ़ॉल्ट समानता जांच का उपयोग करेंगे , इसलिए मुझे कुछ इस तरह की आवश्यकता है,

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

दुर्भाग्य से distinct()विधि में ऐसा कोई अधिभार नहीं है। Personवर्ग के अंदर समानता की जांच को संशोधित किए बिना क्या ऐसा करना संभव है?

जवाबों:


557

पर विचार करें distinctएक होने के लिए स्टेटफुल फिल्टर । यहाँ एक फ़ंक्शन है जो एक विधेय लौटाता है जो इस बारे में स्थिति बनाए रखता है कि यह पहले क्या देखा गया है, और यह बताता है कि क्या दिए गए तत्व को पहली बार देखा गया था:

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

तो आप लिख सकते हैं:

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

ध्यान दें कि यदि धारा का आदेश दिया गया है और समानांतर में चलाया गया है, तो यह पहले वाले के बजाय, डुप्लिकेट के बीच से एक मनमाना तत्व को संरक्षित करेगा distinct()

(यह अनिवार्य रूप से इस प्रश्न के मेरे उत्तर के समान है : जावा लैम्ब्डा स्ट्रीम डिस्टिक्ट () मनमानी कुंजी पर? )


27
मुझे लगता है कि बेहतर संगतता के लिए तर्क होना चाहिए Function<? super T, ?>, नहीं Function<? super T, Object>। यह भी ध्यान दिया जाना चाहिए कि समानांतर धारा के लिए यह समाधान गारंटी नहीं देता है कि कौन सी वस्तु निकाली जाएगी (सामान्य के विपरीत distinct())। इसके अलावा अनुक्रमिक धाराओं के लिए CHM (जो @nosid समाधान में अनुपस्थित है) का उपयोग करने पर अतिरिक्त ओवरहेड है। अंत में यह समाधान filterपद्धति के अनुबंध का उल्लंघन करता है जो कि जावाडॉक में बताए अनुसार अनिवार्य होना चाहिए। फिर भी उखाड़ा गया।
टैगिर वलेव

3
@java_newbie द्वारा दिए गए प्रेडिकेट उदाहरण से इस बात distinctByKeyका कोई अंदाजा नहीं है कि इसका उपयोग समानांतर धारा के भीतर किया जा रहा है या नहीं। यह सीएचएम का उपयोग करता है यदि इसका उपयोग समानांतर में किया जा रहा है, हालांकि यह क्रमिक मामले में ओवरहेड जोड़ता है जैसा कि टैगिर वलेव ने ऊपर उल्लेख किया है।
स्टुअर्ट मार्क्स

5
@holandaGo यह विफल हो जाएगा यदि आप सहेजते हैं और पुन: उपयोग करते हैं, तो इसके द्वारा दिए गए Predicate इंस्टेंस का पुनः उपयोग करें distinctByKey। लेकिन यह काम करता है यदि आप distinctByKeyहर बार कॉल करते हैं, ताकि यह हर बार एक नया प्रेडेक्टेट बना सके।
स्टुअर्ट मार्क्स

3
@ चिनमय नहीं, यह नहीं होना चाहिए। आप का उपयोग करते हैं .filter(distinctByKey(...))। यह एक बार विधि निष्पादित करेगा और विधेय लौटाएगा। तो मूल रूप से मानचित्र पहले से ही फिर से इस्तेमाल किया जा रहा है यदि आप इसे एक धारा के भीतर ठीक से उपयोग करते हैं। यदि आप मानचित्र को स्थिर बनाते हैं, तो मानचित्र सभी उपयोगों के लिए साझा किया जाएगा। इसलिए यदि आपके पास इसका उपयोग करने के लिए दो धाराएँ हैं distinctByKey(), तो दोनों एक ही मानचित्र का उपयोग करेंगे, जो कि आप नहीं चाहते हैं।
g00glen00b

3
यह sooo स्मार्ट और पूरी तरह से गैर-स्पष्ट है। आम तौर पर यह एक स्टेटमेंट लैम्ब्डा है और अंतर्निहित विधि CallSiteसे जुड़ा होगा get$Lambda- जो Predicateहर समय एक नया उदाहरण लौटाएगा , लेकिन उन उदाहरणों को वही mapऔर functionजहां तक ​​मैं समझता हूं, साझा करेंगे। बहुत अच्छा!
यूजीन

152

एक कुंजी के रूप में नाम का उपयोग करके मानचित्र में व्यक्तियों को रखना एक विकल्प होगा:

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

ध्यान दें कि डुप्लिकेट नाम के मामले में, जो व्यक्ति रखा जाता है, वह पहले से जुड़ा हुआ होगा।


23
@skiwi: क्या आपको लगता है distinct()कि उस ओवरहेड के बिना लागू करने का एक तरीका है ? किसी भी कार्यान्वयन को यह कैसे पता चलेगा कि वास्तव में देखे गए सभी विशिष्ट मूल्यों को याद किए बिना किसी वस्तु को देखा है? तो की भूमि के ऊपर toMapऔर distinctबहुत संभावना ही है।
होल्गर

1
@ होल्गर मैं गलत हो सकता था क्योंकि मैंने सोचा नहीं था कि उपरि distinct()खुद बनाता है।
skiwi

2
और स्पष्ट रूप से यह सूची के मूल क्रम को गड़बड़ करता है
फिलिप

10
@Philipp: के लिए बदल रहा द्वारा निर्धारित किया जा सकता हैpersons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
होल्गर

1
@DanielEarwicker यह सवाल "संपत्ति से अलग" के बारे में है। इसके लिए धारा को उसी संपत्ति द्वारा छांटने की आवश्यकता होगी , जो इसका लाभ उठा सके। सबसे पहले, ओपी ने कभी नहीं कहा कि धारा बिल्कुल हल है। दूसरे, धाराएं यह पता लगाने में सक्षम नहीं हैं कि क्या वे एक निश्चित संपत्ति द्वारा छांटे गए हैं । तीसरा, आपके द्वारा सुझाए गए कार्यों को करने के लिए कोई वास्तविक "संपत्ति से अलग" स्ट्रीम ऑपरेशन नहीं है। फोर्थ, व्यवहार में, इस तरह की एक धारा प्राप्त करने के लिए केवल दो तरीके हैं। एक सॉर्ट किया गया स्रोत ( TreeSet) जो पहले से ही अलग है या sortedस्ट्रीम पर जो सभी तत्वों को भी बफ़र करता है।
Holger

101

आप व्यक्ति की वस्तुओं को किसी अन्य वर्ग में लपेट सकते हैं, जो केवल व्यक्तियों के नामों की तुलना करता है। बाद में, आप एक व्यक्ति को फिर से स्ट्रीम प्राप्त करने के लिए लिपटी हुई वस्तुओं को खोल देते हैं। स्ट्रीम ऑपरेशन निम्नानुसार दिख सकते हैं:

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

वर्ग Wrapperनिम्नानुसार लग सकता है:

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


5
@StuartCaie वास्तव में नहीं ... कोई संस्मरण नहीं है, और बिंदु प्रदर्शन नहीं है, लेकिन मौजूदा एपीआई के लिए अनुकूलन है।
मार्को टोपोलनिक

6
com.google.common.base.Equivalence.wrap (S) और com.google.com
bjmi

आप रैपर क्लास को एक मुख्य निष्कर्षण फ़ंक्शन द्वारा सामान्य और पैराट्राइज्ड बना सकते हैं।
Lii

equalsविधि को सरल किया जा सकताreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
होल्गर

55

एक और समाधान, का उपयोग कर Set। आदर्श समाधान नहीं हो सकता है, लेकिन यह काम करता है

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

या यदि आप मूल सूची को संशोधित कर सकते हैं, तो आप removeIf विधि का उपयोग कर सकते हैं

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

2
यदि आप किसी तीसरे पक्ष के पुस्तकालयों का उपयोग नहीं कर रहे हैं तो यह सबसे अच्छा उत्तर है!
मनोज श्रेष्ठ

5
यदि इस सेट में पहले से निर्दिष्ट तत्व नहीं है, तो Set.add सही है कि वास्तविक विचार का उपयोग करना। +1
Luvie

मेरा मानना ​​है कि यह विधि समानांतर धारा प्रसंस्करण के लिए काम नहीं करती है, क्योंकि यह थ्रेड-सुरक्षित नहीं है।
लूबो

@ बीएलओ शायद नहीं। यह सिर्फ एक विचार है, जो सरल मामलों के लिए काम करेगा। उपयोगकर्ता इसे थ्रेड सुरक्षा / समानता के लिए बढ़ा सकते हैं।
संथोश

दिलचस्प दृष्टिकोण, लेकिन एक बाहरी संग्रह को संशोधित करने के लिए एक विरोधी पैटर्न की तरह दिखता है (सेट) दूसरे संग्रह (व्यक्तियों) पर एक धारा को
छानते हुए

31

कस्टम तुलनित्र के साथ ट्रीसेट का उपयोग करने का एक सरल तरीका है।

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

4
मुझे लगता है कि आपका उत्तर आदेश देने में मदद करता है न कि विशिष्टता की ओर। हालाँकि इससे मुझे अपने विचारों को सेट करने में मदद मिली कि इसे कैसे किया जाए। यहाँ देखें: stackoverflow.com/questions/1019854/…
Janagn

ध्यान रखें कि आप यहां तत्वों को क्रमबद्ध करने के लिए मूल्य का भुगतान करेंगे और हमें डुप्लिकेट या यहां तक ​​कि डुप्लिकेट खोजने के लिए छंटनी की आवश्यकता नहीं है।
पिसरुक

12
तुलनित्र.कॉमपरिंग (व्यक्ति :: getName)
जीन-फ्रांस्वा

24

हम RxJava (बहुत शक्तिशाली) का भी उपयोग कर सकते हैं प्रतिक्रियाशील विस्तार पुस्तकालय) का

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

या

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

Rx कमाल है, लेकिन यह एक खराब जवाब है। Observableपुश-आधारित है जबकि Streamपुल-आधारित है। stackoverflow.com/questions/30216979/…
sdgfsdh

4
जरूरी नहीं कि java8 समाधान के लिए स्ट्रीम का उपयोग करके प्रश्न पूछें। मेरा उत्तर बताता है कि java8 स्ट्रीम एपीआई आरएक्स एपीआई की तुलना में कम powefull है
frhack

1
रिएक्टर का उपयोग करना , यह होगाFlux.fromIterable(persons).distinct(p -> p.getName())
रितेश

प्रश्न का शाब्दिक अर्थ है " Streamएपीआई का उपयोग करना ", न कि "आवश्यक रूप से धारा का उपयोग करना"। उस ने कहा, यह धारा को अलग-अलग मानों को फ़िल्टर करने की XY समस्या का एक शानदार समाधान है।
एम। जस्टिन

12

आप groupingByकलेक्टर का उपयोग कर सकते हैं :

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

यदि आप एक और धारा रखना चाहते हैं तो आप इसका उपयोग कर सकते हैं:

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

11

आप ग्रहण संग्रहोंdistinct(HashingStrategy) में विधि का उपयोग कर सकते हैं ।

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

यदि आप personsएक ग्रहण संग्रह इंटरफ़ेस को लागू करने के लिए रिफ्लेक्टर कर सकते हैं , तो आप विधि को सीधे सूची में कॉल कर सकते हैं।

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

HashingStrategy बस एक रणनीति इंटरफ़ेस है जो आपको समान और हैशकोड के कस्टम कार्यान्वयन को परिभाषित करने की अनुमति देता है।

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

नोट: मैं एक्लिप्स कलेक्शंस के लिए कमिट हूं।


विधि संग्रह को ग्रहण संग्रह 9.0 में जोड़ा गया था जो इस समाधान को और सरल बना सकता है। medium.com/@donraab/…
डोनाल्ड रब

10

मैं Vavr का उपयोग करने की सलाह देता हूं , यदि आप कर सकते हैं। इस पुस्तकालय के साथ आप निम्नलिखित कार्य कर सकते हैं:

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

पूर्व में "जवासलंग" पुस्तकालय के रूप में जाना जाता था।
user11153

9

आप StreamEx लाइब्रेरी का उपयोग कर सकते हैं :

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

दुर्भाग्य से, अन्यथा भययोग्य StreamEx लाइब्रेरी की यह विधि खराब डिज़ाइन की गई है - यह बराबरी का उपयोग करने के बजाय ऑब्जेक्ट समानता की तुलना करती है। यह Stringस्ट्रिंग इंटर्न के लिए धन्यवाद के लिए काम कर सकता है , लेकिन यह भी नहीं हो सकता है।
टोक़

7

स्टुअर्ट मार्क्स के जवाब का विस्तार, यह एक छोटे तरीके से और समवर्ती नक्शे के बिना किया जा सकता है (यदि आपको समानांतर धाराओं की आवश्यकता नहीं है):

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

फिर कॉल करो:

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

2
यह विचार नहीं करता है कि धारा समानांतर हो सकती है।
ब्रुनेस्बे

टिप्पणी के लिए धन्यवाद, मैंने अपना उत्तर अपडेट कर दिया है। यदि आपको समानांतर धारा की आवश्यकता नहीं है, तो समवर्ती नक्शे का उपयोग नहीं करने से आपको बहुत बेहतर प्रदर्शन मिलता है।
वोज्शिएक गोर्की

यदि आप Collections.synchronizedSet(new HashSet<>())इसके बजाय बनाया तो आपका कोड शायद समानांतर संग्रह के लिए काम करेगा । लेकिन यह शायद के साथ की तुलना में धीमी होगी ConcurrentHashMap
Lii

7

इसी तरह का दृष्टिकोण जो सईद ज़ारिनफाम ने इस्तेमाल किया लेकिन अधिक जावा 8 शैली :)

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

1
मैं इसके साथ मैप लाइन को बदल flatMap(plans -> plans.stream().findFirst().stream())
दूंगा

शायद यह भी ठीक है: फ्लैटपाइप (योजनाएं -> योजनाएं) () (सीमा। (1))
आरआरआर

6

मैंने एक सामान्य संस्करण बनाया:

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

एक छूट:

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

6

एक अन्य पुस्तकालय जो इसका समर्थन करता है वह है jOOλ , और इसकी Seq.distinct(Function<T,U>)विधि:

Seq.seq(persons).distinct(Person::getName).toList();

हुड के तहत , यह व्यावहारिक रूप से स्वीकृत जवाब के रूप में एक ही बात करता है ।



5

मेरा दृष्टिकोण यह है कि सभी वस्तुओं को एक ही संपत्ति के साथ समूहित करें, फिर समूहों को 1 के आकार में छोटा करें और फिर अंत में उन्हें एक के रूप में इकट्ठा करें List

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

3

विशिष्ट वस्तुओं की सूची का उपयोग कर पाया जा सकता है:

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

2

इसे लागू करने का सबसे आसान तरीका है सॉर्ट सुविधा पर कूदना क्योंकि यह पहले से ही एक वैकल्पिक प्रदान करता है Comparatorजिसे किसी तत्व की संपत्ति का उपयोग करके बनाया जा सकता है। फिर आपको डुप्लिकेट को फ़िल्टर करना होगा जो एक स्टेटफुल का उपयोग करके किया जा सकता है Predicateजो इस तथ्य का उपयोग करता है कि एक सॉर्ट किए गए स्ट्रीम के लिए सभी समान तत्व आसन्न हैं:

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

बेशक, एक स्टेटफुल Predicateधागा-सुरक्षित नहीं है, हालांकि अगर आपकी ज़रूरत है तो आप इस तर्क को एक में स्थानांतरित कर सकते हैंCollector और धारा का उपयोग करते समय थ्रेड-सुरक्षा का ध्यान रख सकते हैं Collector। यह इस बात पर निर्भर करता है कि आप अलग-अलग तत्वों की धारा के साथ क्या करना चाहते हैं, जो आपने हमें अपने प्रश्न में नहीं बताया था।


1

@ Josketres के उत्तर पर बिल्डिंग, मैंने एक सामान्य उपयोगिता विधि बनाई:

आप एक कलेक्टर बनाकर इसे और अधिक जावा 8-अनुकूल बना सकते हैं ।

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


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

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

private class C {
    public final int value;

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

1

शायद किसी के लिए उपयोगी होगा। मुझे थोड़ी और आवश्यकता थी। A3rd पार्टी से ऑब्जेक्ट्स की सूची होने पर सभी को समान के A.bलिए समान फ़ील्ड हटा दें A.id( सूची में Aसमान के साथ कई ऑब्जेक्ट A.id)। टैगिर वलेव द्वारा स्ट्रीम विभाजन के उत्तर ने मुझे कस्टम का उपयोग करने के लिए प्रेरित किया जो रिटर्न देता है । बाकी काम सिंपल करेंगे।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 कुंजी के आधार पर सूची से अलग तत्व प्राप्त करने के लिए मान लिया गया था। यदि आप दो कुंजी के आधार पर अलग-अलग चाहते हैं या समग्र कुंजी कर सकते हैं, तो यह प्रयास करें

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


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

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

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

0

मेरे मामले में मुझे यह नियंत्रित करने की आवश्यकता थी कि पिछला तत्व क्या था। मैंने तब एक स्टेटिक प्रेडिकेट बनाया, जहां मैंने नियंत्रित किया था कि अगर पिछला तत्व मौजूदा तत्व से अलग था, तो उस स्थिति में मैंने इसे रखा था।

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

public class LogPredicate implements Predicate<Log> {

    private Log previous;

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

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

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

}

0

इस सूची में मेरा समाधान:

List<HolderEntry> result ....

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

मेरी स्थिति में मैं अलग-अलग मूल्यों को खोजना चाहता हूं और उनकी सूची में रखना चाहता हूं।


0

जबकि उच्चतम उत्तोलित उत्तर बिल्कुल जावा जावा 8 का सबसे अच्छा उत्तर है, यह प्रदर्शन के मामले में बिल्कुल खराब है। यदि आप वास्तव में खराब प्रदर्शन करने वाले आवेदन चाहते हैं, तो आगे बढ़ें और इसका उपयोग करें। व्यक्ति के नाम का एक अनूठा सेट निकालने की सरल आवश्यकता "केवल प्रत्येक" और एक "सेट" द्वारा प्राप्त की जाएगी। अगर सूची 10 के आकार से ऊपर है तो चीजें और भी खराब हो जाती हैं।

विचार करें कि आपके पास 20 वस्तुओं का संग्रह है, जैसे:

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

जहाँ आप वस्तु SimpleEventइस तरह दिखती है:

public class SimpleEvent {

private String name;
private String type;

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

public String getName() {
    return name;
}

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

public String getType() {
    return type;
}

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

और परीक्षण करने के लिए, आपके पास इस तरह का जेएमएच कोड है, (कृपया ध्यान दें, स्वीकार किए गए उत्तर में उल्लिखित एक ही अलग-अलग उपयोग करके im करें ):

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

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

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

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

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

    new Runner(opt).run();
}

फिर आपके पास बेंचमार्क परिणाम इस तरह होंगे:

Benchmark                                  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

और जैसा कि आप देख सकते हैं, जावा 8 स्ट्रीम की तुलना में, थ्रूपुट में एक साधारण For- 3 अधिक बेहतर है और त्रुटि स्कोर में कम है।

उच्चतर थ्रूपुट, बेहतर प्रदर्शन


1
धन्यवाद, लेकिन प्रश्न विशेष रूप से स्ट्रीम एपीआई के संदर्भ में
रिचके

हां, मैं सहमत हूं, मैंने पहले ही उल्लेख किया है "जबकि उच्चतम उत्तोलन का उत्तर बिल्कुल जावा एक्स 8 सबसे अच्छा उत्तर है"। एक समस्या को अलग-अलग तरीकों से हल किया जा सकता है, और यहाँ पर प्रकाश डालने की कोशिश कर रहा है कि Java 8 धाराओं के साथ खतरनाक तरीके से, बजाय खतरे को हल किया जा सकता है, जहाँ खतरे का प्रदर्शन कम हो रहा है। :)
अभिनव गांगुली

0
Here is the example
public class PayRoll {

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


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

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

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

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


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


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



    }
}

Output:

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

-2

यदि आप व्यक्तियों की सूची चाहते हैं तो निम्नलिखित सरल तरीका होगा

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

इसके अतिरिक्त, यदि आप किसी व्यक्ति की नहीं , बल्कि विशिष्ट या विशिष्ट नामों की सूची प्राप्त करना चाहते हैं, तो आप निम्न दो तरीकों का उपयोग करके भी कर सकते हैं।

विधि 1: का उपयोग कर distinct

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

विधि 2: का उपयोग कर HashSet

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

2
यह नामों की एक सूची तैयार करता है, Personएस नहीं ।
हल्क

1
यही वह है जिसकी तलाश में मैं हूं। मुझे एक संग्रह को एक दूसरे में बदलने के दौरान डुप्लिकेट को खत्म करने के लिए एकल लाइन विधि की आवश्यकता थी। धन्यवाद।
राज

-3

सबसे सरल कोड आप लिख सकते हैं:

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

12
हालांकि, नामों की एक अलग सूची मिल जाएगी, नाम से नहीं व्यक्तियों
अमीर 13
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.