मतभेदों के लिए दो सामान्य सूचियों की तुलना करने का सबसे तेज़ तरीका


214

दो बड़े पैमाने पर (> 50.000 आइटम) की तुलना में सबसे तेज (और कम से कम संसाधन गहन) क्या है और परिणामस्वरूप दो सूची हैं जैसे:

  1. आइटम जो पहली सूची में दिखाई देते हैं लेकिन दूसरे में नहीं
  2. आइटम जो दूसरी सूची में दिखाई देते हैं लेकिन पहले में नहीं

वर्तमान में मैं सूची या IReadOnlyCollection के साथ काम कर रहा हूं और इस समस्या को एक लाइन क्वेरी में हल कर रहा हूं:

var list1 = list.Where(i => !list2.Contains(i)).ToList();
var list2 = list2.Where(i => !list.Contains(i)).ToList();

लेकिन यह उतना अच्छा प्रदर्शन नहीं करता जितना मैं चाहूंगा। जैसा कि मुझे बहुत सी सूचियों को संसाधित करने की आवश्यकता है, इस तेज और कम संसाधन को गहन बनाने का कोई विचार नहीं है?

जवाबों:


454

उपयोग करें Except:

var firstNotSecond = list1.Except(list2).ToList();
var secondNotFirst = list2.Except(list1).ToList();

मुझे संदेह है कि ऐसे दृष्टिकोण हैं जो वास्तव में इससे कहीं अधिक तेजी से होंगे, लेकिन यहां तक ​​कि यह आपके ओ (एन * एम) दृष्टिकोण की तुलना में बहुत तेजी से होगा ।

यदि आप इन्हें संयोजित करना चाहते हैं, तो आप उपरोक्त विधि और फिर एक रिटर्न स्टेटमेंट बना सकते हैं:

return !firstNotSecond.Any() && !secondNotFirst.Any();

टिप्पणी करने के लिए एक मतलब नहीं है कि है है प्रश्न में मूल कोड और यहाँ समाधान के बीच परिणामों में एक फर्क: किसी भी डुप्लिकेट तत्व है जो केवल एक ही सूची में हैं ही, जबकि वे कई के रूप में रिपोर्ट दी जाएगी, मेरे कोड के साथ एक बार सूचित किया जाएगा समय के रूप में वे मूल कोड में होते हैं।

उदाहरण के लिए, की सूची के साथ [1, 2, 2, 2, 3]और [1], मूल कोड में "सूची 1 लेकिन सूची 2 में तत्व" परिणाम नहीं होगा [2, 2, 2, 3]। मेरे कोड के साथ यह सिर्फ होगा [2, 3]। कई मामलों में यह एक मुद्दा नहीं होगा, लेकिन इसके बारे में जागरूक होने के लायक है।


8
यह वास्तव में एक बहुत बड़ा प्रदर्शन है! इस उत्तर के लिए धन्यवाद।
फ्रैंक

2
मैं दो विशाल सूचियों के लिए सोच रहा हूं, क्या तुलना करने से पहले इसे छांटना उपयोगी है? या विस्तार विधि को छोड़कर, अंदर पारित सूची पहले से ही क्रमबद्ध है।
लैरी

9
@ लॉरी: यह हल नहीं है; यह एक हैश सेट बनाता है।
जॉन स्कीट

2
@PranavSingh: यह उचित समानता वाले किसी भी चीज़ के लिए काम करेगा - इसलिए यदि आपका कस्टम प्रकार ओवरराइड होता है Equals(object)और / या लागू होता है तो IEquatable<T>यह ठीक होना चाहिए।
जॉन स्कीट

2
@ k2ibegin: यह डिफ़ॉल्ट समानता तुलनित्र का उपयोग करता है, जो एक IEquatable<T>कार्यान्वयन या object.Equals(object)विधि का उपयोग करेगा । ऐसा लगता है कि आपको कम से कम प्रतिलिपि प्रस्तुत करने योग्य उदाहरण के साथ एक नया प्रश्न बनाना चाहिए - हम वास्तव में टिप्पणियों में चीजों का निदान नहीं कर सकते हैं।
जॉन स्कीट

40

अधिक कुशल उपयोग होगा Enumerable.Except:

var inListButNotInList2 = list.Except(list2);
var inList2ButNotInList = list2.Except(list);

यह विधि आस्थगित निष्पादन का उपयोग करके कार्यान्वित की जाती है। इसका मतलब है कि आप उदाहरण के लिए लिख सकते हैं:

var first10 = inListButNotInList2.Take(10);

यह भी कुशल है क्योंकि यह आंतरिक रूप Set<T>से वस्तुओं की तुलना करने के लिए उपयोग करता है । यह पहले दूसरे अनुक्रम से सभी अलग-अलग मानों को इकट्ठा करके काम करता है, और फिर पहले के परिणामों को स्ट्रीमिंग करता है, यह जांचता है कि उन्हें पहले नहीं देखा गया है।


1
हम्म। काफी टालमटोल नहीं की। मैं आंशिक रूप से स्थगित कहूंगा। एक पूर्ण Set<T>दूसरे अनुक्रम से बनाया गया है (यानी यह पूरी तरह से पुनरावृत्त और संग्रहीत है), फिर पहले अनुक्रम से जोड़े जा सकने वाले आइटमों का उत्पादन किया जाता है।
खर्चा करने

2
@spender, यह कहना कि निष्पादन Whereको आंशिक रूप से स्थगित कर दिया गया है क्योंकि list.Where(x => x.Id == 5)संख्या के मूल्य 5को प्रारंभ में संग्रहीत किया जाता है, बजाय आलसी निष्पादित किए।
jwg

27

Enumerable.SequenceEqual विधि

यह निर्धारित करता है कि एक समानता तुलना के अनुसार दो अनुक्रम समान हैं या नहीं। MS.Docs

Enumerable.SequenceEqual(list1, list2);

यह सभी आदिम डेटा प्रकारों के लिए काम करता है। यदि आपको इसे कस्टम ऑब्जेक्ट्स पर उपयोग करने की आवश्यकता है जिसे आपको लागू करने की आवश्यकता हैIEqualityComparer

समानता के लिए वस्तुओं की तुलना का समर्थन करने के तरीकों को परिभाषित करता है।

IEqualityComparer इंटरफ़ेस

समानता के लिए वस्तुओं की तुलना का समर्थन करने के तरीकों को परिभाषित करता है। MS.ocs IEqualityComparer के लिए


यह स्वीकृत उत्तर होना चाहिए। प्रश्न SETS के बारे में नहीं है बल्कि LISTS के बारे में है, जिसमें तत्वों का दोहराव हो सकता है।
बजे एड्रियन नासुई

3
मैं नहीं देखता कि यह कैसे उत्तर हो सकता है, यह देखते हुए कि इसका परिणाम SequenceEqualएक सरल है bool। ओपी परिणामों की दो सूचियाँ चाहता है - और यह बताता है कि वे सेट संचालन के संदर्भ में क्या चाहते हैं: "आइटम जो पहली सूची में दिखाई देते हैं लेकिन दूसरे में नहीं"। कोई संकेत नहीं है कि आदेश प्रासंगिक है, जबकि SequenceEqual है करता है यह विचार करना प्रासंगिक हो। यह एक बिल्कुल अलग प्रश्न का उत्तर देता प्रतीत होता है।
जॉन स्कीट

हाँ, सही है, ऐसा लगता है कि मैंने इसका उत्तर बहुत तेज़ी से दिया और अनुरोध के दूसरे भाग को नहीं देखा ... पहले दो टिप्पणियों के समान ...
मिगुएलम्पन

9

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

List<string> list1 = new List<string> { "a.dll", "b1.dll" };
List<string> list2 = new List<string> { "A.dll", "b2.dll" };

var firstNotSecond = list1.Except(list2, StringComparer.OrdinalIgnoreCase).ToList();
var secondNotFirst = list2.Except(list1, StringComparer.OrdinalIgnoreCase).ToList();

firstNotSecondइसमें b1.dll शामिल होगा

secondNotFirstb2.dll होगा


5

इस समस्या के लिए नहीं, लेकिन समान और नहीं के लिए सूचियों की तुलना करने के लिए यहां कुछ कोड है! समान वस्तुएं:

public class EquatableList<T> : List<T>, IEquatable<EquatableList<T>> where    T : IEquatable<T>

/// <summary>
/// True, if this contains element with equal property-values
/// </summary>
/// <param name="element">element of Type T</param>
/// <returns>True, if this contains element</returns>
public new Boolean Contains(T element)
{
    return this.Any(t => t.Equals(element));
}

/// <summary>
/// True, if list is equal to this
/// </summary>
/// <param name="list">list</param>
/// <returns>True, if instance equals list</returns>
public Boolean Equals(EquatableList<T> list)
{
    if (list == null) return false;
    return this.All(list.Contains) && list.All(this.Contains);
}

1
यह वह है जो आपको कस्टम डेटा प्रकारों की तुलना करने में सक्षम होना चाहिए। तब का उपयोगExcept
प्रणव सिंह

आप संभवतः सॉर्ट करने योग्य प्रकारों के साथ बेहतर कर सकते हैं। यह O (n ^ 2) में चलता है, जबकि आप O (nlogn) कर सकते हैं।
युवलम्म २

3

इस तरह से प्रयास करें:

var difList = list1.Where(a => !list2.Any(a1 => a1.id == a.id))
            .Union(list2.Where(a => !list1.Any(a1 => a1.id == a.id)));

13
यह भयानक प्रदर्शन से ग्रस्त है, पहले में प्रत्येक आइटम के लिए दूसरी सूची के स्कैन की आवश्यकता होती है। काम नहीं कर रहा है क्योंकि यह काम करता है, लेकिन यह मूल कोड के रूप में बुरा है।
खर्चा करने

3
using System.Collections.Generic;
using System.Linq;

namespace YourProject.Extensions
{
    public static class ListExtensions
    {
        public static bool SetwiseEquivalentTo<T>(this List<T> list, List<T> other)
            where T: IEquatable<T>
        {
            if (list.Except(other).Any())
                return false;
            if (other.Except(list).Any())
                return false;
            return true;
        }
    }
}

कभी-कभी आपको केवल यह जानने की आवश्यकता होती है कि क्या दो सूचियाँ अलग हैं, और न कि वे अंतर क्या हैं। उस स्थिति में, इस विस्तार विधि को अपनी परियोजना में जोड़ने पर विचार करें। ध्यान दें कि आपकी सूचीबद्ध वस्तुओं को IEquatable को लागू करना चाहिए!

उपयोग:

public sealed class Car : IEquatable<Car>
{
    public Price Price { get; }
    public List<Component> Components { get; }

    ...
    public override bool Equals(object obj)
        => obj is Car other && Equals(other);

    public bool Equals(Car other)
        => Price == other.Price
            && Components.SetwiseEquivalentTo(other.Components);

    public override int GetHashCode()
        => Components.Aggregate(
            Price.GetHashCode(),
            (code, next) => code ^ next.GetHashCode()); // Bitwise XOR
}

जो भी Componentवर्ग है, उसके लिए यहां दिखाए गए तरीकों Carको लगभग समान रूप से लागू किया जाना चाहिए।

यह ध्यान रखना बहुत ज़रूरी है कि हमने GetHashCode को कैसे लिखा है। ठीक से लागू करने के लिए IEquatable, Equalsऔर एक तार्किक रूप से उदाहरण के गुणों पर काम GetHashCode करना चाहिए

समान सामग्री वाली दो सूचियां अभी भी अलग-अलग ऑब्जेक्ट हैं, और अलग-अलग हैश कोड का उत्पादन करेंगी। चूंकि हम चाहते हैं कि इन दोनों सूचियों को समान माना जाए, इसलिए हमें GetHashCodeउनमें से प्रत्येक के लिए समान मूल्य का उत्पादन करने देना चाहिए । हम सूची में हर तत्व को हैशकोड सौंपकर, और उन सभी को संयोजित करने के लिए मानक बिटवाइड XOR का उपयोग करके इसे पूरा कर सकते हैं। XOR ऑर्डर-अज्ञेयवादी है, इसलिए यह कोई फर्क नहीं पड़ता कि सूचियों को अलग-अलग क्रमबद्ध किया गया है। यह केवल मायने रखता है कि उनके पास समतुल्य सदस्यों के अलावा कुछ नहीं है।

नोट: अजीब नाम इस तथ्य का अर्थ है कि विधि सूची में तत्वों के आदेश पर विचार नहीं करती है। यदि आप सूची में तत्वों के क्रम के बारे में परवाह करते हैं, तो यह विधि आपके लिए नहीं है!


1

मैंने दो कोड की तुलना करने के लिए इस कोड का उपयोग किया है जिसमें लाखों रिकॉर्ड हैं।

इस विधि में ज्यादा समय नहीं लगेगा

    //Method to compare two list of string
    private List<string> Contains(List<string> list1, List<string> list2)
    {
        List<string> result = new List<string>();

        result.AddRange(list1.Except(list2, StringComparer.OrdinalIgnoreCase));
        result.AddRange(list2.Except(list1, StringComparer.OrdinalIgnoreCase));

        return result;
    }

0

यदि केवल संयुक्त परिणाम की जरूरत है, तो यह भी काम करेगा:

var set1 = new HashSet<T>(list1);
var set2 = new HashSet<T>(list2);
var areEqual = set1.SetEquals(set2);

जहाँ T सूची तत्व का प्रकार है।


-1

यह मज़ेदार हो सकता है, लेकिन मेरे लिए काम करता है

string.Join ("", सूची 1)! = string.Join ("", सूची 2)


जैसा कि यहां लिखा गया है कि यह सूची <string> या List <int> के लिए भी काम नहीं करेगा, उदाहरण के लिए दो सूचियाँ 11; 2; 3; और 1; 12; 3 समान होगी क्योंकि आप कुछ के साथ तार में शामिल नहीं होते हैं। अद्वितीय विभाजक जो सूची में संभव आइटम नहीं है। इसके अलावा, कई मदों के साथ एक सूची के लिए समवर्ती स्ट्रिंग्स शायद एक प्रदर्शन हत्यारा है।
स्विसकोडर

@SwissCoder: आप गलत हैं, यह स्ट्रिंग के लिए एक प्रदर्शन करने वाला हत्यारा नहीं है। यदि आपके पास 50.000 स्ट्रिंग्स (लंबाई 3 में से प्रत्येक) के साथ दो सूची है, तो इस एल्गोरिथ्म को मेरी मशीन पर 3 एमएस की आवश्यकता है। स्वीकृत उत्तर की जरूरत है 7. मुझे लगता है कि ट्रिक है जिब्ज़ को केवल एक स्ट्रिंग तुलना की आवश्यकता है। बेशक उसे एक अनोखा विभाजक जोड़ना होगा।
user1027167

@ user1027167: Im सीधे तार की तुलना करने की बात नहीं कर रहा है (क्योंकि यह भी सवाल नहीं है)। 50.000 ऑब्जेक्ट्स के साथ सूची में सभी ऑब्जेक्ट्स की .ToString () पद्धति को कॉल करना एक बहुत बड़ी स्ट्रिंग बना सकता है, यह निर्भर करता है कि यह कैसे लागू होता है। मुझे नहीं लगता कि यह रास्ता है। फिर किसी चरित्र या स्ट्रिंग पर "अद्वितीय" होने का भरोसा करना भी जोखिम भरा है, कोड वास्तव में इस तरह पुन: प्रयोज्य नहीं होगा।
स्विस कोड

ठीक है, यह सच है। प्रश्नकर्ता ने अपनी सूचियों का डेटाटाइप दिए बिना सबसे तेज़ तरीका पूछा। संभवतः यह उत्तरदाता प्रश्नकर्ता के उपयोग के मामले का सबसे तेज तरीका है।
user1027167

-3

मुझे लगता है कि यह तत्व द्वारा दो सूचियों तत्व की तुलना करने का एक सरल और आसान तरीका है

x=[1,2,3,5,4,8,7,11,12,45,96,25]
y=[2,4,5,6,8,7,88,9,6,55,44,23]

tmp = []


for i in range(len(x)) and range(len(y)):
    if x[i]>y[i]:
        tmp.append(1)
    else:
        tmp.append(0)
print(tmp)

3
यह एक C # प्रश्न है, और आपने C # कोड प्रदान नहीं किया है।
वाई हा ली

1
शायद आप इस उत्तर को हटा सकते हैं और इसे ले जा सकते हैं (उदाहरण के लिए) मैं अजगर और वापसी मैचों में दो सूचियों की तुलना कैसे कर सकता हूं ?
वाई हा ली

-4

यह सबसे अच्छा समाधान है जो आपको मिल जाएगा

var list3 = list1.Where(l => list2.ToList().Contains(l));

1
यह वास्तव में बहुत बुरा है क्योंकि यह List<T>प्रत्येक तत्व के लिए एक नया बनाता है list1। परिणाम भी कहा जाता है list3जब यह नहीं है List<T>
वाई हा ली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.