जावा में दो सेटों की तुलना करने का सबसे तेज़ तरीका क्या है?


102

मैं कोड के एक टुकड़े को अनुकूलित करने की कोशिश कर रहा हूं जो सूची के तत्वों की तुलना करता है।

उदाहरण के लिए।

public void compare(Set<Record> firstSet, Set<Record> secondSet){
    for(Record firstRecord : firstSet){
        for(Record secondRecord : secondSet){
            // comparing logic
        }
    }
}

कृपया ध्यान रखें कि सेटों में रिकॉर्ड की संख्या अधिक होगी।

धन्यवाद

शेखर


7
तुलनात्मक तर्क को जाने (और संशोधित) किए बिना छोरों को अनुकूलित करना संभव नहीं है। क्या आप अपना अधिक कोड दिखा सकते हैं?
जोसेफ

जवाबों:


161
firstSet.equals(secondSet)

यह वास्तव में इस बात पर निर्भर करता है कि आप तुलनात्मक तर्क में क्या करना चाहते हैं ... यदि एक तत्व को दूसरे में नहीं मिला तो क्या होगा? आपकी विधि में एक voidवापसी प्रकार है, इसलिए मुझे लगता है कि आप इस विधि में आवश्यक कार्य करेंगे।

अधिक सूक्ष्म दानेदार नियंत्रण यदि आपको इसकी आवश्यकता हो तो:

if (!firstSet.containsAll(secondSet)) {
  // do something if needs be
}
if (!secondSet.containsAll(firstSet)) {
  // do something if needs be
}

यदि आपको उन तत्वों को प्राप्त करने की आवश्यकता है जो एक सेट में हैं और दूसरे में नहीं।
संपादित करें: set.removeAll(otherSet)एक सेट नहीं, एक बूलियन लौटाता है। निष्कासन () का उपयोग करने के लिए, आपको सेट को कॉपी करना होगा और फिर उसका उपयोग करना होगा।

Set one = new HashSet<>(firstSet);
Set two = new HashSet<>(secondSet);
one.removeAll(secondSet);
two.removeAll(firstSet);

यदि सामग्री oneऔर twoदोनों खाली हैं, तो आप जानते हैं कि दोनों सेट समान थे। यदि नहीं, तो आप उन तत्वों को प्राप्त कर चुके हैं जो सेट को असमान बनाते हैं।

आपने उल्लेख किया कि रिकॉर्ड की संख्या अधिक हो सकती है। यदि अंतर्निहित कार्यान्वयन एक है, HashSetतो प्रत्येक रिकॉर्ड को लाने का काम O(1)समय पर किया जाता है , इसलिए आप वास्तव में इससे बेहतर नहीं हो सकते। TreeSetहै O(log n)


3
रिकॉर्ड क्लास के लिए समान () और हैशकोड () का कार्यान्वयन समान रूप से महत्वपूर्ण है, जब सेट पर बराबर () को लागू किया जाता है।
विनीत रेनॉल्ड्स

1
मुझे यकीन नहीं है कि removeAll () के उदाहरण सही हैं। निष्कासन () एक बूलियन देता है, दूसरा सेट नहीं। सेकंडसेट में मौजूद तत्वों को वास्तव में पहले सेट से हटा दिया जाता है और यदि कोई परिवर्तन किया गया है तो सच लौटा दिया जाता है।
रिचर्ड कोरफील्ड

4
निष्कासन का उदाहरण अभी भी सही नहीं है क्योंकि आपने प्रतियां नहीं बनाई हैं (एक सेट करें = पहला सेट; दो सेट = दूसरा सेट)। मैं कॉपी कंस्ट्रक्टर का उपयोग करूंगा।
माइकल रसच

1
दरअसल, सबसे खराब स्थिति में equalsदो कॉल की तुलना में डिफ़ॉल्ट कार्यान्वयन तेजी से containsAllहोता है; मेरा जवाब देखिए।
स्टीफन सी।

6
आपको सेट एक = नया हैशसेट (फ़र्स्टसेट) करने की ज़रूरत है, अन्यथा फ़र्स्टसेट और सेकंडसेट के आइटम हट जाएंगे।
Bonton255

61

यदि आप बस जानना चाहते हैं कि क्या सेट बराबर हैं, तो equalsविधि AbstractSetको नीचे के रूप में लागू किया गया है:

    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Set))
            return false;
        Collection c = (Collection) o;
        if (c.size() != size())
            return false;
        return containsAll(c);
    }

ध्यान दें कि यह आम मामलों का अनुकूलन कैसे करता है:

  • दो वस्तुएं समान हैं
  • दूसरी वस्तु बिल्कुल भी सेट नहीं है, और
  • दो सेट के आकार अलग हैं।

उसके बाद, जैसे ही यह दूसरे सेट में एक तत्व पाता है, जो इस सेट में भी नहीं है, containsAll(...)वापस आ जाएगा false। लेकिन अगर सभी तत्व दोनों सेटों में मौजूद हैं, तो उन सभी का परीक्षण करने की आवश्यकता होगी।

सबसे खराब स्थिति प्रदर्शन तब होता है जब दो सेट समान होते हैं लेकिन समान वस्तु नहीं होती है। वह लागत आमतौर पर O(N)या O(NlogN)कार्यान्वयन के आधार पर होती है this.containsAll(c)

और अगर आप सेट बड़े हैं और तत्वों के एक छोटे प्रतिशत में भिन्न होते हैं, तो आपको सबसे खराब स्थिति मिलती है।


अपडेट करें

यदि आप कस्टम सेट कार्यान्वयन में समय लगाने के इच्छुक हैं, तो एक दृष्टिकोण है जो "लगभग समान" मामले में सुधार कर सकता है।

विचार यह है कि आपको पूरे सेट के लिए एक हैश पूर्व-गणना और कैश करने की आवश्यकता है ताकि आप सेट के वर्तमान हैशकोड को प्राप्त कर सकें O(1)। फिर आप एक त्वरण के रूप में दो सेट के लिए हैशकोड की तुलना कर सकते हैं।

आप इस तरह से हैशकोड कैसे लागू कर सकते हैं? खैर अगर सेट हैशकोड था:

  • एक खाली सेट के लिए शून्य, और
  • गैर-खाली सेट के लिए सभी तत्व हैशकोड का XOR,

तब आप सस्ते में सेट के कैश्ड हैशकोड को हर बार अपडेट कर सकते हैं जब आप एक तत्व को जोड़ते या हटाते हैं। दोनों ही मामलों में, आप बस वर्तमान सेट हैशकोड के साथ तत्व के हैशकोड को XOR करते हैं।

बेशक, यह मानता है कि तत्व हैशकोड स्थिर हैं, जबकि तत्व सेट के सदस्य हैं। यह भी मानता है कि तत्व वर्ग हैशकोड फ़ंक्शन एक अच्छा प्रसार देता है। ऐसा इसलिए है क्योंकि जब दो सेट हैशकोड समान होते हैं तब भी आपको O(N)सभी तत्वों की तुलना में वापस आना पड़ता है ।


आप इस विचार को थोड़ा और आगे ले जा सकते हैं ... कम से कम सिद्धांत में।

चेतावनी - यह अत्यधिक सट्टा है। यदि आप चाहें तो एक "विचार प्रयोग"।

मान लें कि आपके सेट तत्व वर्ग में तत्व के लिए एक क्रिप्टो चेकसम वापस करने की विधि है। अब तत्वों के लिए लौटे चेकसमों को XORING द्वारा सेट के चेकसमों को लागू करें।

यह हमें क्या खरीदता है?

ठीक है, अगर हम मानते हैं कि कुछ भी नहीं चल रहा है, तो संभावना है कि किसी भी दो असमान सेट तत्वों में समान एन-बिट चेकसम 2- एन है । और संभावना 2 असमान सेटों में समान एन-बिट चेकसम भी 2- एन है । इसलिए मेरा विचार यह है कि आप इसे लागू कर सकते हैं equals:

    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Set))
            return false;
        Collection c = (Collection) o;
        if (c.size() != size())
            return false;
        return checksums.equals(c.checksums);
    }

उपरोक्त मान्यताओं के तहत, यह आपको केवल 2- एन समय में एक बार गलत उत्तर देगा । यदि आप N को पर्याप्त बड़ा बनाते हैं (जैसे 512 बिट्स) तो एक गलत उत्तर की संभावना नगण्य हो जाती है (जैसे लगभग 10 -150 )।

नकारात्मक पक्ष यह है कि तत्वों के लिए क्रिप्टो चेकसमों की गणना करना बहुत महंगा है, खासकर जब बिट्स की संख्या बढ़ जाती है। तो आप वास्तव में चेकसम याद करने के लिए एक प्रभावी तंत्र की जरूरत है। और यह समस्याग्रस्त हो सकता है।

और अन्य नकारात्मक पक्ष यह है कि त्रुटि की एक गैर-शून्य संभावना अस्वीकार्य हो सकती है, चाहे कितनी भी छोटी संभावना हो। (लेकिन अगर यह मामला है ... आप उस मामले से कैसे निपटते हैं जहां एक लौकिक किरण एक महत्वपूर्ण बिट को फ़्लिप करती है? या यदि यह एक ही तरह से एक बेमानी प्रणाली के दो उदाहरणों में फ़्लिप करता है?)


ऐसा होना चाहिए अगर (checkumsDoNotMatch (0)) गलत लौट आए; और वापसी doHeavyComparisonToMakeSureTheSetsReallyMatch (o);
Esko Piirainen

जरुरी नहीं। यदि गैर-समान सेटों के लिए दो चेकसमों के मिलान की संभावना कम है, तो मैं पर्याप्त मानता हूं कि आप तुलना छोड़ सकते हैं। आकलन करो।
स्टीफन सी

17

अमरूद में एक विधि है Setsजो यहां मदद कर सकती है:

public static <E>  boolean equals(Set<? extends E> set1, Set<? extends E> set2){
return Sets.symmetricDifference(set1,set2).isEmpty();
}

5

आपके पास https://www.mkyong.com/java/java-how-to-compare-two-sets/ से निम्न समाधान है

public static boolean equals(Set<?> set1, Set<?> set2){

    if(set1 == null || set2 ==null){
        return false;
    }

    if(set1.size() != set2.size()){
        return false;
    }

    return set1.containsAll(set2);
}

या यदि आप सिंगल रिटर्न स्टेटमेंट का उपयोग करना पसंद करते हैं:

public static boolean equals(Set<?> set1, Set<?> set2){

  return set1 != null 
    && set2 != null 
    && set1.size() == set2.size() 
    && set1.containsAll(set2);
}

या शायद बस (जेडीके के साथ भेज दिया) equals()से विधि का उपयोग करें AbstractSetजो अतिरिक्त नल चेक को छोड़कर लगभग समाधान के समान है । जावा -11 सेट इंटरफ़ेस
चैथू नारायण

4

बहुत विशिष्ट मामलों के लिए एक O (N) समाधान है जहां:

  • सेट दोनों क्रमबद्ध हैं
  • दोनों एक ही क्रम में क्रमबद्ध

निम्नलिखित कोड मानता है कि दोनों सेट तुलनीय रिकॉर्ड के आधार पर हैं। एक समान विधि एक तुलनित्र पर आधारित हो सकती है।

    public class SortedSetComparitor <Foo extends Comparable<Foo>> 
            implements Comparator<SortedSet<Foo>> {

        @Override
        public int compare( SortedSet<Foo> arg0, SortedSet<Foo> arg1 ) {
            Iterator<Foo> otherRecords = arg1.iterator();
            for (Foo thisRecord : arg0) {
                // Shorter sets sort first.
                if (!otherRecords.hasNext()) return 1;
                int comparison = thisRecord.compareTo(otherRecords.next());
                if (comparison != 0) return comparison;
            }
            // Shorter sets sort first
            if (otherRecords.hasNext()) return -1;
            else return 0;
        }
    }

3

यदि आप Guavaपुस्तकालय का उपयोग कर रहे हैं, तो यह करना संभव है:

        SetView<Record> added = Sets.difference(secondSet, firstSet);
        SetView<Record> removed = Sets.difference(firstSet, secondSet);

और फिर इन के आधार पर एक निष्कर्ष निकालें।


2

मैं तुलना करने से पहले दूसरे सैट को हाशप में डालूंगा। इस तरह आप दूसरी सूची के खोज समय को घटाकर n (1) कर देंगे। ऐशे ही:

HashMap<Integer,Record> hm = new HashMap<Integer,Record>(secondSet.size());
int i = 0;
for(Record secondRecord : secondSet){
    hm.put(i,secondRecord);
    i++;
}
for(Record firstRecord : firstSet){
    for(int i=0; i<secondSet.size(); i++){
    //use hm for comparison
    }
}

या आप दूसरी सूची के लिए हैशमैप के बजाय सरणी का उपयोग कर सकते हैं।
साहिन हेबसोग्लू

और, यह समाधान मानता है कि सेट को क्रमबद्ध नहीं किया गया है।
साहिन हैबसोग्लू

1
public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Set))
            return false;

        Set<String> a = this;
        Set<String> b = o;
        Set<String> thedifference_a_b = new HashSet<String>(a);


        thedifference_a_b.removeAll(b);
        if(thedifference_a_b.isEmpty() == false) return false;

        Set<String> thedifference_b_a = new HashSet<String>(b);
        thedifference_b_a.removeAll(a);

        if(thedifference_b_a.isEmpty() == false) return false;

        return true;
    }

-1

मुझे लगता है कि समान पद्धति के साथ विधि संदर्भ का उपयोग किया जा सकता है। हम मानते हैं कि किसी संदेह की छाया के बिना ऑब्जेक्ट प्रकार की अपनी तुलना विधि है। सादा और सरल उदाहरण यहाँ है,

Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));

Set<String> set2 = new HashSet<>();
set2.addAll(Arrays.asList("hanks","leo","bale"));

Predicate<Set> pred = set::equals;
boolean result = pred.test(set2);
System.out.println(result);   // true

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