मैं दो ArrayLists के बीच अंतर की गणना कैसे कर सकता हूं?


81

मेरे पास दो ArrayLists हैं।

ArrayList A में है:

['2009-05-18','2009-05-19','2009-05-21']

ArrayList B में शामिल हैं:

['2009-05-18','2009-05-18','2009-05-19','2009-05-19','2009-05-20','2009-05-21','2009-05-21','2009-05-22']

मुझे ArrayList A और ArrayList B की तुलना करनी है। ArrayList के परिणाम में वह सूची होनी चाहिए जो ArrayList A में मौजूद नहीं है।

ArrayList परिणाम होना चाहिए:

['2009-05-20','2009-05-22']

तुलना कैसे करें?

जवाबों:


194

जावा में, आप Collectionइंटरफ़ेस की removeAllविधि का उपयोग कर सकते हैं ।

// Create a couple ArrayList objects and populate them
// with some delicious fruits.
Collection firstList = new ArrayList() {{
    add("apple");
    add("orange");
}};

Collection secondList = new ArrayList() {{
    add("apple");
    add("orange");
    add("banana");
    add("strawberry");
}};

// Show the "before" lists
System.out.println("First List: " + firstList);
System.out.println("Second List: " + secondList);

// Remove all elements in firstList from secondList
secondList.removeAll(firstList);

// Show the "after" list
System.out.println("Result: " + secondList);

उपरोक्त कोड निम्नलिखित आउटपुट का उत्पादन करेगा:

First List: [apple, orange]
Second List: [apple, orange, banana, strawberry]
Result: [banana, strawberry]

7
यदि आपकी सूची एक कस्टम वर्ग की है, तो आपको अपनी कक्षा के बराबर विधि को ओवरराइड करना होगा, है ना?
आरटीएफ

5
@ आरटीएफ हां, आपको इसका कार्यान्वयन प्रदान करने की आवश्यकता equalsहै जो आपकी वस्तुओं की तुलना करने में सक्षम बनाता है। लागू करने के बारे hashCodeमें भी पढ़ें । उदाहरण के लिए, ध्यान दें कि मामला संवेदनशील कैसे String::equalsहै , इसलिए "सेब" और "सेब" को एक ही नहीं माना जाएगा।
बेसिल बोर्के

1
दरअसल जवाब इस बात पर निर्भर करता है कि आप क्या करना चाहते हैं। RemoveAll डुप्लिकेट को संरक्षित नहीं करेगा। यदि आप अपनी दूसरी सूची में एक और "सेब" स्ट्रिंग जोड़ते हैं, तो इसे भी हटा दिया जाएगा, जो हमेशा वह नहीं हो सकता जो आप चाहते हैं।
जूल्स टेस्टार्ड

2
यह इतना अक्षम है। यह दुख की बात है कि यह चयनित और सर्वश्रेष्ठ मूल्यांकित उत्तर दोनों है। removeAllके firstList.containsहर तत्व पर कॉल करता है secondList। A का उपयोग करने से HashSetउस पर रोक लगेगी और कुछ अच्छे उत्तर कम होंगे।
वलासेक

20

आपके पास पहले से ही सही जवाब है। और यदि आप सूचियों (संग्रहों) के बीच अधिक जटिल और रोचक संचालन करना चाहते हैं, तो अपाचे कॉमन्स संग्रह ( कलेक्शन यूटिल्स ) का उपयोग करें यह आपको संयोजन / विघटन बनाने, चौराहे खोजने की अनुमति देता है, जांचें कि क्या एक संग्रह दूसरे और अन्य अच्छी चीजों का सबसेट है।


मावेन / ग्रैडल
Wlad

12

जावा 8 में स्ट्रीम के साथ, यह वास्तव में बहुत सरल है। संपादित करें: धाराओं के बिना कुशल हो सकता है, कम देखें।

List<String> listA = Arrays.asList("2009-05-18","2009-05-19","2009-05-21");
List<String> listB = Arrays.asList("2009-05-18","2009-05-18","2009-05-19","2009-05-19",
                                   "2009-05-20","2009-05-21","2009-05-21","2009-05-22");

List<String> result = listB.stream()
                           .filter(not(new HashSet<>(listA)::contains))
                           .collect(Collectors.toList());

ध्यान दें कि हैश सेट केवल एक बार बनाया जाता है: विधि संदर्भ में इसकी विधि शामिल है। लैम्ब्डा के साथ ऐसा करने से सेट को एक चर में रखने की आवश्यकता होगी। एक चर बनाना एक बुरा विचार नहीं है, खासकर यदि आप इसे समझने में भद्दा या कठिन पाते हैं।

आप आसानी से इस उपयोगिता विधि (या स्पष्ट डाली) की तरह कुछ के बिना विधेय को नकार नहीं सकते हैं , क्योंकि आप सीधे नकारात्मक विधि संदर्भ को कॉल नहीं कर सकते हैं (प्रकार पहले अनुमान की आवश्यकता है)।

private static <T> Predicate<T> not(Predicate<T> predicate) {
    return predicate.negate();
}

यदि धाराओं में एक filterOutविधि या कुछ और होता है, तो यह अच्छा लगेगा।


साथ ही, @ होल्गर ने मुझे एक विचार दिया। ArrayListइसकी removeAllविधि कई निष्कासन के लिए अनुकूलित है, यह केवल एक बार अपने तत्वों को पुनर्व्यवस्थित करता है। हालांकि, यह containsदिए गए संग्रह द्वारा प्रदान की गई विधि का उपयोग करता है , इसलिए हमें उस हिस्से को अनुकूलित करने की आवश्यकता है यदि listAकुछ भी लेकिन छोटे हैं।

पहले listAऔर listBघोषित के साथ , इस समाधान को जावा 8 की आवश्यकता नहीं है और यह बहुत ही कुशल है।

List<String> result = new ArrayList(listB);
result.removeAll(new HashSet<>(listA));

1
@ Bax क्यों संपादित करें? मूल क्लीनर और कार्यात्मक रूप से समान था।
shmosel

1
@ बैक्स नहीं, यह नहीं है।
shmosel

1
अमरूद के साथ, आप कर सकते हैं Predicates.in(new HashSet<>(listA)).negate()
शमोसेल

1
मैं बस कुछ परीक्षण चलाता हूं और यह समाधान listB.removeAll (नई HashSet <> (listA)) की तुलना में ~ 10-20% तेज है। और अमरुद सेट.डिफरेंस (...) सी 2 बार धाराओं की तुलना में धीमा है।
15

1
@ वाल्सेक ArrayList.removeमें रैखिक जटिलता है, लेकिन ArrayList.removeAllभरोसा नहीं removeकरता है लेकिन एक रैखिक सरणी अपडेट ऑपरेशन करता है, प्रत्येक तत्व को अपने अंतिम स्थान पर कॉपी करता है। इसके विपरीत, संदर्भ कार्यान्वयन का LinkedListकोई अनुकूलित नहीं है, removeAllलेकिन removeप्रत्येक प्रभावित तत्व के लिए एक ऑपरेशन करता है , जो हर बार पांच संदर्भों तक अद्यतन होगा। तो, हटा दिया और शेष तत्वों के बीच अनुपात पर निर्भर करता है, ArrayListकी removeAllअभी भी काफी की तुलना में बेहतर प्रदर्शन कर सकते हैं LinkedListरों, यहां तक कि बड़ी सूचियों के लिए '।
होल्गर

9

संपादित करें: मूल प्रश्न में भाषा निर्दिष्ट नहीं थी। मेरा उत्तर C # में है।

आपको इस उद्देश्य के लिए HashSet का उपयोग करना चाहिए। यदि आपको ArrayList का उपयोग करना चाहिए, तो आप निम्नलिखित एक्सटेंशन विधियों का उपयोग कर सकते हैं:

var a = arrayListA.Cast<DateTime>();
var b = arrayListB.Cast<DateTime>();    
var c = b.Except(a);

var arrayListC = new ArrayList(c.ToArray());

हैशसेट का उपयोग ...

var a = new HashSet<DateTime>(); // ...and fill it
var b = new HashSet<DateTime>(); // ...and fill it
b.ExceptWith(a); // removes from b items that are in a

8

मैंने अमरूद सेट्स का उपयोग किया है ।

मापदंडों सेट और सामान्य नहीं संग्रह, लेकिन एक आसान तरीका किसी भी संग्रह से सेट बनाने के (अद्वितीय आइटम के साथ) अमरूद है ImmutableSet.copyOf (Iterable)।

(मैंने पहले इसे संबंधित / डुप्लीकेट प्रश्न पर पोस्ट किया था , लेकिन मैं इसे यहां भी कॉपी कर रहा हूं क्योंकि मुझे लगता है कि यह एक अच्छा विकल्प है जो अब तक गायब है।)


8

हालाँकि यह जावा 8 में एक बहुत पुराना सवाल है, लेकिन आप ऐसा कुछ कर सकते हैं

 List<String> a1 = Arrays.asList("2009-05-18", "2009-05-19", "2009-05-21");
 List<String> a2 = Arrays.asList("2009-05-18", "2009-05-18", "2009-05-19", "2009-05-19", "2009-05-20", "2009-05-21","2009-05-21", "2009-05-22");

 List<String> result = a2.stream().filter(elem -> !a1.contains(elem)).collect(Collectors.toList());

मुझे जावा 8 से प्यार है, लेकिन हमें अभी भी जटिलता के बारे में सोचना चाहिए। जबकि सूचियों में भी Collectionविधि है contains, यह बहुत अक्षम है। नहीं मिलने पर इसे पूरी सूची से गुजरना होगा। हर तत्व के लिए ऐसा करने से a2बड़ी सूचियों पर दर्द कम हो सकता है, यही कारण है कि मैं a1अपने उत्तर में एक सेट बना लेता हूं।
वलसेक

2

मुझे लगता है कि आप C # के बारे में बात कर रहे हैं। यदि हां, तो आप यह कोशिश कर सकते हैं

    ArrayList CompareArrayList(ArrayList a, ArrayList b)
    {
        ArrayList output = new ArrayList();
        for (int i = 0; i < a.Count; i++)
        {
            string str = (string)a[i];
            if (!b.Contains(str))
            {
                if(!output.Contains(str)) // check for dupes
                    output.Add(str);
            }
        }
        return output;
    }

क्षमा करें, मैंने बधाई देने वाली भाषा का उल्लेख नहीं किया, यह ठीक है, लेकिन मुझे उर रिप्ले के लिए जावा धन्यवाद की आवश्यकता है
नवीन

यह सही है। हालांकि यह इसे करने का एक बहुत ही अक्षम तरीका भी है। आप मूल रूप से पूरी bसूची a.Countसमय के माध्यम से चक्र करेंगे । आप सेट के लिए HashSetउपयोग करने के लिए Containsया RemoveAllसेट विधि का उपयोग करने के बजाय एक परिणाम बना सकते हैं जो आप चाहते हैं।
वलासेक

1

आप सिर्फ तार की तुलना कर रहे हैं।

ArrayList A में कुंजियों को HashTable A में कुंजी के रूप में
रखें। ArrayList B में मान को HashTable B में कुंजियों के रूप में रखें।

फिर, हैशटेबल ए में प्रत्येक कुंजी के लिए, हैशटेबल बी से इसे हटा दें यदि यह मौजूद है।

हैशटेबल बी में आपके पास जो चीजें बची हैं, वे स्ट्रिंग्स (कीज़) हैं जो एरेलेस्ट ए में मूल्य नहीं थे।

कोड के अनुरोध के जवाब में C # (3.0) उदाहरण जोड़ा गया:

List<string> listA = new List<string>{"2009-05-18","2009-05-19","2009-05-21'"};
List<string> listB = new List<string>{"2009-05-18","2009-05-18","2009-05-19","2009-05-19","2009-05-20","2009-05-21","2009-05-21","2009-05-22"};

HashSet<string> hashA = new HashSet<string>();
HashSet<string> hashB = new HashSet<string>();

foreach (string dateStrA in listA) hashA.Add(dateStrA);
foreach (string dateStrB in listB) hashB.Add(dateStrB);

foreach (string dateStrA in hashA)
{
    if (hashB.Contains(dateStrA)) hashB.Remove(dateStrA);
}

List<string> result = hashB.ToList<string>();

आपके C # कोड में, hashAचर प्रभावी रूप से बेकार है। आप listAइसके बजाय hashAकेवल इसके द्वारा iterated है और Containsकभी नहीं कहा जाता है के साथ एक आड़ू बना सकता है ।
वलसेक

(इसके अलावा, बशर्ते कि C # में एक RemoveAll पद्धति हो, जैसा कि जावा करता है, आप अपना खुद का चक्र बनाने से बच सकते हैं ... लेकिन फिर, मैंने आपको अपवित्र कर दिया क्योंकि यह समाधान चयनित एक की तुलना में कम से कम अधिक कुशल है।)
Vlasec

1

हाय इस वर्ग का उपयोग करें यह दोनों सूचियों की तुलना करेगा और दोनों सूची को बिल्कुल बेमेल / डब्ल्यू दिखाता है।

import java.util.ArrayList;
import java.util.List;


public class ListCompare {

    /**
     * @param args
     */
    public static void main(String[] args) {
        List<String> dbVinList;
        dbVinList = new ArrayList<String>();
        List<String> ediVinList;
        ediVinList = new ArrayList<String>();           

        dbVinList.add("A");
        dbVinList.add("B");
        dbVinList.add("C");
        dbVinList.add("D");

        ediVinList.add("A");
        ediVinList.add("C");
        ediVinList.add("E");
        ediVinList.add("F");
        /*ediVinList.add("G");
        ediVinList.add("H");
        ediVinList.add("I");
        ediVinList.add("J");*/  

        List<String> dbVinListClone = dbVinList;
        List<String> ediVinListClone = ediVinList;

        boolean flag;
        String mismatchVins = null;
        if(dbVinListClone.containsAll(ediVinListClone)){
            flag = dbVinListClone.removeAll(ediVinListClone);   
            if(flag){
                mismatchVins = getMismatchVins(dbVinListClone);
            }
        }else{
            flag = ediVinListClone.removeAll(dbVinListClone);
            if(flag){
                mismatchVins = getMismatchVins(ediVinListClone);
            }
        }
        if(mismatchVins != null){
            System.out.println("mismatch vins : "+mismatchVins);
        }       

    }

    private static String getMismatchVins(List<String> mismatchList){
        StringBuilder mismatchVins = new StringBuilder();
        int i = 0;
        for(String mismatch : mismatchList){
            i++;
            if(i < mismatchList.size() && i!=5){
                mismatchVins.append(mismatch).append(",");  
            }else{
                mismatchVins.append(mismatch);
            }
            if(i==5){               
                break;
            }
        }
        String mismatch1;
        if(mismatchVins.length() > 100){
            mismatch1 = mismatchVins.substring(0, 99);
        }else{
            mismatch1 = mismatchVins.toString();
        }       
        return mismatch1;
    }

}

क्या आप जानते हैं कि क्लोन वास्तव में क्लोन नहीं हैं?
वलासेक

1

Arraylist के साथ यह काम भी करें

    // Create a couple ArrayList objects and populate them
    // with some delicious fruits.
    ArrayList<String> firstList = new ArrayList<String>() {/**
         * 
         */
        private static final long serialVersionUID = 1L;

    {
        add("apple");
        add("orange");
        add("pea");
    }};

    ArrayList<String> secondList = new ArrayList<String>() {

    /**
         * 
         */
        private static final long serialVersionUID = 1L;

    {
        add("apple");
        add("orange");
        add("banana");
        add("strawberry");
    }};

    // Show the "before" lists
    System.out.println("First List: " + firstList);
    System.out.println("Second List: " + secondList);

    // Remove all elements in firstList from secondList
    secondList.removeAll(firstList);

    // Show the "after" list
    System.out.println("Result: " + secondList);

1
आउटपुट: पहली सूची: [सेब, नारंगी, पिप्पो] दूसरी सूची: [सेब, नारंगी, केला, स्ट्रॉबेरी] परिणाम: [केला, स्ट्रॉबेरी]
मनो

ऐसा होता है। लेकिन जब आप ऐसा कहते हैं, तो आपको यह ध्यान नहीं देना चाहिए कि यह बड़ी सूचियों में धीमी गति से हो सकता है। मन के तरीकों की तरह है कि में भालू removeऔर containsपूरी सूची के माध्यम से खोज करने के लिए की जरूरत है। यदि एक चक्र में बार-बार कहा जाता है (जो होता है removeAll), तो आपको एक द्विघात जटिलता मिलती है। हालाँकि आप हैश सेट का उपयोग कर सकते हैं और यह केवल रैखिक है।
वलासेक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.