जावा 8, डुप्लिकेट तत्वों को खोजने के लिए स्ट्रीम करता है


87

मैं पूर्णांक सूची में डुप्लिकेट तत्वों को सूचीबद्ध करने की कोशिश कर रहा हूं जैसे कि,

List<Integer> numbers = Arrays.asList(new Integer[]{1,2,1,3,4,4});    

Jdk 8 की धाराओं का उपयोग करना। क्या किसी ने कोशिश की है। डुप्लिकेट को निकालने के लिए हम विशिष्ट () एपीआई का उपयोग कर सकते हैं। लेकिन डुप्लिकेट तत्वों को खोजने के बारे में क्या? कोई भी मेरी मदद कर सकता है?



यदि आप स्ट्रीम एकत्र नहीं करना चाहते हैं, तो यह अनिवार्य रूप से "मैं एक स्ट्रीम में एक से अधिक आइटम को कैसे देख सकता हूं" के लिए नीचे फोड़ा जाता है?
थोरबजोरन राव एंडरसन

सेट करें <Integer> items = new HashSet (); number.stream ()। filter (n -> i! tems.add (n))। collect (संग्राहक.toSet ());
सरोज कुमार साहू

जवाबों:


127

आप उपयोग कर सकते हैं Collections.frequency:

numbers.stream().filter(i -> Collections.frequency(numbers, i) >1)
                .collect(Collectors.toSet()).forEach(System.out::println);

11
@ OussamaZoghlami उत्तर में समान O (n ^ 2) प्रदर्शन , हालांकि संभवतः सरल है। फिर भी यहाँ एक उत्थान है। StackOverflow में आपका स्वागत है!
टैगिर वलेव

6
जैसा कि उल्लेख किया गया है, यह एक ^ 2 समाधान है जहां एक तुच्छ रैखिक समाधान मौजूद है। मैं सीआर में यह स्वीकार नहीं करेंगे।
jwilner

3
यह @Dave विकल्प से धीमा हो सकता है, लेकिन यह प्रिटियर है इसलिए मैं प्रदर्शन को हिट करूंगा।
jDub9

@jwner n ^ 2 समाधान के बारे में अपनी बात है जो एक फिल्टर में कलेक्शंस.फ्रीक्वेंसी के उपयोग का जिक्र है?
मैनकैपैक

5
@mancocapac हां, यह द्विघात है क्योंकि आवृत्ति कॉल को संख्या में प्रत्येक तत्व का दौरा करना पड़ता है, और इसे प्रत्येक तत्व पर कॉल किया जा रहा है। इस प्रकार, प्रत्येक तत्व के लिए, हम प्रत्येक तत्व पर जाते हैं - n ^ 2 और अनावश्यक रूप से अक्षम।
jwilner

72

मूल उदाहरण। फ़र्स्ट-हाफ़ फ़्रीक्वेंसी-मैप बनाता है, सेकंड-हाफ़ इसे फ़िल्टर की गई सूची में कम कर देता है। शायद डेव के उत्तर के रूप में कुशल नहीं है, लेकिन अधिक बहुमुखी (जैसे कि यदि आप वास्तव में दो का पता लगाना चाहते हैं आदि)।

     List<Integer> duplicates = IntStream.of( 1, 2, 3, 2, 1, 2, 3, 4, 2, 2, 2 )
       .boxed()
       .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) )
       .entrySet()
       .stream()
       .filter( p -> p.getValue() > 1 )
       .map( Map.Entry::getKey )
       .collect( Collectors.toList() );

12
यह उत्तर सही एक imo है क्योंकि यह रैखिक है और "स्टेटलेस प्रेडिकेटेट" नियम का उल्लंघन नहीं करता है।
जूलर

55

allItemsसंपूर्ण सरणी सामग्री को रखने के लिए आपको एक सेट ( नीचे) की आवश्यकता है, लेकिन यह O (n) है:

Integer[] numbers = new Integer[] { 1, 2, 1, 3, 4, 4 };
Set<Integer> allItems = new HashSet<>();
Set<Integer> duplicates = Arrays.stream(numbers)
        .filter(n -> !allItems.add(n)) //Set.add() returns false if the item was already in the set.
        .collect(Collectors.toSet());
System.out.println(duplicates); // [1, 4]

18
filter()के लिए एक राज्य की आवश्यकता होती है। आपका "समाधान" आश्चर्यजनक ढंग से जावाडोक में दिए गए स्टेटफुल विधेय का उदाहरण के समान है: docs.oracle.com/javase/8/docs/api/java/util/stream/...
मैट मैकहेनरी

1
@MattMcHenry: इसका मतलब है कि इस समाधान में अप्रत्याशित व्यवहार पैदा करने की क्षमता है, या यह सिर्फ बुरा अभ्यास है?
IDDante

7
@IcedDante वहां के स्थानीयकृत मामले में जहां आप यह सुनिश्चित करने के लिए जानते हैं कि स्ट्रीम है sequential(), यह संभवतः सुरक्षित है। अधिक सामान्य मामले में जहां धारा हो सकती है parallel(), यह बहुत अजीब तरीके से टूटने की गारंटी है।
मैट मैकहेनरी

5
कुछ स्थितियों में अप्रत्याशित व्यवहार उत्पन्न करने के अलावा, यह प्रतिमानों को मिश्रित करता है क्योंकि बलोच का तर्क है कि आपको प्रभावी जावा के तीसरे संस्करण में नहीं होना चाहिए। यदि आप अपने आप को यह लिख रहे हैं, तो बस एक लूप का उपयोग करें।
jwilner

6
हाइबरनेट वैलिडेटर यूनीलाइज़ेशन बाधा द्वारा उपयोग किए जा रहे जंगली में यह पाया गया ।
डेव

14

एक O (n) तरीका इस प्रकार होगा:

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 4, 4);
Set<Integer> duplicatedNumbersRemovedSet = new HashSet<>();
Set<Integer> duplicatedNumbersSet = numbers.stream().filter(n -> !duplicatedNumbersRemovedSet.add(n)).collect(Collectors.toSet());

अंतरिक्ष की जटिलता इस दृष्टिकोण में दोगुनी हो जाएगी, लेकिन वह स्थान बेकार नहीं है; वास्तव में, अब हमारे पास केवल एक सेट के साथ-साथ दूसरे डुप्लिकेट को भी हटा दिया गया है।


13

मेरी स्ट्रीमटेक्स लाइब्रेरी जो जावा 8 स्ट्रीम को बढ़ाती है, एक विशेष ऑपरेशन प्रदान करती है distinct(atLeast)जो कम से कम निर्दिष्ट समय में प्रदर्शित होने वाले तत्वों को बनाए रख सकती है। तो आपकी समस्या इस तरह हल हो सकती है:

List<Integer> repeatingNumbers = StreamEx.of(numbers).distinct(2).toList();

आंतरिक रूप से यह @Dave समाधान के समान है, यह वस्तुओं को गिनता है, अन्य वांछित मात्राओं का समर्थन करने के लिए और यह समानांतर-अनुकूल है (यह ConcurrentHashMapसमानांतर धारा के लिए उपयोग करता है, लेकिन HashMapअनुक्रमिक के लिए)। बड़ी मात्रा में डेटा के लिए आप स्पीड-अप का उपयोग कर प्राप्त कर सकते हैं .parallel().distinct(2)


26
सवाल जावा स्ट्रीम के बारे में है, न कि थर्ड-पार्टी लाइब्रेरी के बारे में।

9

आप इस तरह से डुप्लिकेट प्राप्त कर सकते हैं:

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 4, 4);
Set<Integer> duplicated = numbers
  .stream()
  .filter(n -> numbers
        .stream()
        .filter(x -> x == n)
        .count() > 1)
   .collect(Collectors.toSet());

11
क्या वह O (n ^ 2) ऑपरेशन नहीं है?
तर्जुक

4
उपयोग करने की कोशिश करेंnumbers = Arrays.asList(400, 400, 500, 500);
टैगिर वलेव

1
क्या यह 2 गहराई पाश बनाने के समान है? for (..) {for (..)} बस क्यूरियोस आंतरिक रूप से यह कैसे काम करता है
redigaffi

हालांकि यह एक अच्छा दृष्टिकोण है, फिर भी streamअंदर streamहोना महंगा है।
विश्व रत्न

4

मुझे लगता है कि प्रश्न के मूल समाधान नीचे दिए गए होने चाहिए:

Supplier supplier=HashSet::new; 
HashSet has=ls.stream().collect(Collectors.toCollection(supplier));

List lst = (List) ls.stream().filter(e->Collections.frequency(ls,e)>1).distinct().collect(Collectors.toList());

ठीक है, यह एक फिल्टर ऑपरेशन करने के लिए अनुशंसित नहीं है, लेकिन बेहतर समझ के लिए, मैंने इसे इस्तेमाल किया है, इसके अलावा, भविष्य के संस्करणों में कुछ कस्टम निस्पंदन होना चाहिए।


3

एक मल्टीसेट एक संरचना है जो प्रत्येक तत्व के लिए घटनाओं की संख्या को बनाए रखता है। अमरूद कार्यान्वयन का उपयोग:

Set<Integer> duplicated =
        ImmutableMultiset.copyOf(numbers).entrySet().stream()
                .filter(entry -> entry.getCount() > 1)
                .map(Multiset.Entry::getElement)
                .collect(Collectors.toSet());

2

एक अतिरिक्त मानचित्र या स्ट्रीम का निर्माण समय- और अंतरिक्ष की खपत है ...

Set<Integer> duplicates = numbers.stream().collect( Collectors.collectingAndThen(
  Collectors.groupingBy( Function.identity(), Collectors.counting() ),
  map -> {
    map.values().removeIf( cnt -> cnt < 2 );
    return( map.keySet() );
  } ) );  // [1, 4]


... और जिस प्रश्न के लिए दावा किया जाता है कि एक [डुप्लिकेट]

public static int[] getDuplicatesStreamsToArray( int[] input ) {
  return( IntStream.of( input ).boxed().collect( Collectors.collectingAndThen(
      Collectors.groupingBy( Function.identity(), Collectors.counting() ),
      map -> {
        map.values().removeIf( cnt -> cnt < 2 );
        return( map.keySet() );
      } ) ).stream().mapToInt( i -> i ).toArray() );
}

1

यदि आपको केवल डुप्लिकेट की उपस्थिति का पता लगाने की आवश्यकता है (उन्हें सूचीबद्ध करने के बजाय, जो ओपी चाहता था), बस उन्हें सूची और सेट दोनों में बदल दें, फिर आकारों की तुलना करें:

    List<Integer> list = ...;
    Set<Integer> set = new HashSet<>(list);
    if (list.size() != set.size()) {
      // duplicates detected
    }

मुझे यह दृष्टिकोण पसंद है क्योंकि इसमें गलतियों के लिए कम स्थान हैं।


0

मुझे लगता है कि मेरे पास अच्छा समाधान है कि इस तरह की समस्या को कैसे ठीक किया जाए - सूची => कुछ और द्वारा समूह के साथ सूची। & Some.b। विस्तारित परिभाषा है:

public class Test {

    public static void test() {

        class A {
            private int a;
            private int b;
            private float c;
            private float d;

            public A(int a, int b, float c, float d) {
                this.a = a;
                this.b = b;
                this.c = c;
                this.d = d;
            }
        }


        List<A> list1 = new ArrayList<A>();

        list1.addAll(Arrays.asList(new A(1, 2, 3, 4),
                new A(2, 3, 4, 5),
                new A(1, 2, 3, 4),
                new A(2, 3, 4, 5),
                new A(1, 2, 3, 4)));

        Map<Integer, A> map = list1.stream()
                .collect(HashMap::new, (m, v) -> m.put(
                        Objects.hash(v.a, v.b, v.c, v.d), v),
                        HashMap::putAll);

        list1.clear();
        list1.addAll(map.values());

        System.out.println(list1);
    }

}

वर्ग A, list1 यह सिर्फ आने वाला डेटा है - जादू Objects.hash (...) :) में है


1
चेतावनी: यदि Objects.hashसमान मूल्य के लिए (v.a_1, v.b_1, v.c_1, v.d_1)और (v.a_2, v.b_2, v.c_2, v.d_2), तो वे समान माने जा रहे हैं और डुप्लिकेट के रूप में निकाले जा रहे हैं, वास्तव में यह जांचे बिना कि ए, बी, सी, और डी के समान हैं। यह एक स्वीकार्य जोखिम हो सकता है, या आप एक फ़ंक्शन का उपयोग करना चाह सकते हैं, Objects.hashजिसके अलावा आपके डोमेन पर एक अद्वितीय परिणाम बनाने की गारंटी हो।
मार्टी नील

0

क्या आपको जावा 8 मुहावरों (स्टीम) का उपयोग करना है? पेरैप्स एक सरल समाधान जटिलता को एक नक्शे डेटा संरचना में स्थानांतरित करने के लिए होगा जो संख्याओं को कुंजी के रूप में रखता है (दोहराए बिना) और समय इसे मान के रूप में ocurrs करता है। आप उन्हें बता सकते हैं कि मानचित्र केवल उन संख्याओं के साथ कुछ करता है जो ऑकुर> 1 हैं।

import java.lang.Math;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;

public class RemoveDuplicates
{
  public static void main(String[] args)
  {
   List<Integer> numbers = Arrays.asList(new Integer[]{1,2,1,3,4,4});
   Map<Integer,Integer> countByNumber = new HashMap<Integer,Integer>();
   for(Integer n:numbers)
   {
     Integer count = countByNumber.get(n);
     if (count != null) {
       countByNumber.put(n,count + 1);
     } else {
       countByNumber.put(n,1);
     }
   }
   System.out.println(countByNumber);
   Iterator it = countByNumber.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry pair = (Map.Entry)it.next();
        System.out.println(pair.getKey() + " = " + pair.getValue());
    }
  }
}

0

इस समाधान का प्रयास करें:

public class Anagramm {

public static boolean isAnagramLetters(String word, String anagramm) {
    if (anagramm.isEmpty()) {
        return false;
    }

    Map<Character, Integer> mapExistString = CharCountMap(word);
    Map<Character, Integer> mapCheckString = CharCountMap(anagramm);
    return enoughLetters(mapExistString, mapCheckString);
}

private static Map<Character, Integer> CharCountMap(String chars) {
    HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>();
    for (char c : chars.toCharArray()) {
        if (charCountMap.containsKey(c)) {
            charCountMap.put(c, charCountMap.get(c) + 1);
        } else {
            charCountMap.put(c, 1);
        }
    }
    return charCountMap;
}

static boolean enoughLetters(Map<Character, Integer> mapExistString, Map<Character,Integer> mapCheckString) {
    for( Entry<Character, Integer> e : mapCheckString.entrySet() ) {
        Character letter = e.getKey();
        Integer available = mapExistString.get(letter);
        if (available == null || e.getValue() > available) return false;
    }
    return true;
}

}

0

इंडेक्स की जाँच के बारे में क्या?

        numbers.stream()
            .filter(integer -> numbers.indexOf(integer) != numbers.lastIndexOf(integer))
            .collect(Collectors.toSet())
            .forEach(System.out::println);

1
ठीक काम करना चाहिए, लेकिन यहां कुछ अन्य समाधानों के रूप में ओ (एन ^ 2) प्रदर्शन भी।
फ्लोरियन अल्ब्रेक्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.