java - कलर - इस इश्क में मर जावा नाटक




संपत्ति द्वारा जावा 8 विशिष्ट (11)

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

आप Collector बनाने के द्वारा इसे और अधिक जावा 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;
    }
}

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

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

persons.stream().distinct();

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

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

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


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

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

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

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

यदि आप एक और स्ट्रीम चाहते हैं तो आप इसका उपयोग कर सकते हैं:

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

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

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

इसे लागू करने का सबसे आसान तरीका सॉर्ट फीचर पर कूदना है क्योंकि यह पहले से ही एक वैकल्पिक 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 का उपयोग करते समय थ्रेड-सुरक्षा की देखभाल कर सकते हैं। यह इस बात पर निर्भर करता है कि आप उन विशिष्ट तत्वों की धारा के साथ क्या करना चाहते हैं जिन्हें आपने हमें अपने प्रश्न में नहीं बताया था।


एक राज्यव्यापी फिल्टर होने के लिए 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() करता है।

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


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

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

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

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

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

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

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

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

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




java-stream