遍历 2 个列表

Posted

技术标签:

【中文标题】遍历 2 个列表【英文标题】:Iterate through 2 Lists 【发布时间】:2011-03-18 13:49:33 【问题描述】:

我有一个 List<T1> 的项目和第二个 List<T2> 的项目。两个列表都按属性 A 的字母顺序排序。我知道 List<T2> 中的项目列表是 List<T1> 的子集,List<T2> 中不存在 List<T1> 中不存在的项目。

我需要遍历List<T1> 并在每次匹配List<T2> 中的变量时更改一个变量。最快最好的方法是什么?我假设我需要遍历这两个列表,但我知道做一个嵌套的 foreach 没有意义。

【问题讨论】:

列表是否属于同一类型? 列表有多长?如果我们谈论的是微小的数字,不要排除一些非常简单粗略的 O(n^2) 解决方案。 from x in List1 join y in List2 on x.P equals y.P? 几百,但它正在进入一个网页。此外,这些列表是不同的类型,对可比较的变量具有相同的属性/方法调用。 【参考方案1】:

对于这种类型的事情,我更喜欢双循环 for 循环。请参阅下面的示例。

var super = new List<Contact>();
super.Add(new Contact() Name = "John");
super.Add(new Contact() Name = "Larry");
super.Add(new Contact() Name = "Smith");
super.Add(new Contact() Name = "Corey");

var sub = new List<Contact>();
sub.Add(new Contact() Name = "Larry");
sub.Add(new Contact() Name = "Smith");

var subCount = 0;
for(int i=0; i<super.Count && subCount < sub.Count; i++)

    if (super[i].Name == sub[subCount].Name)
    
        Act(super[i], sub[subCount]);
        subCount++;
    

Act(...) 执行您想要执行的任何操作。

循环每次都会增加超级列表,但只有在找到匹配项时才会增加子列表。

请注意,这只适用于您的两个假设。 1) 列表均已排序,并且 2) 第二个列表是第一个列表的子集。

【讨论】:

起初我以为这是错误的。但是有了subsuper 的子集的信息,这是我的一个更清洁的解决方案,它只假设排序,因此必须处理跳过错过的匹配。虽然这不能处理具有相同属性值的多个条目。 对。这些假设对于这种方法很重要。 该方法将遍历每个超级列表项的每个子列表项。这意味着它循环 N*M 次,其中 N 和 M 是超级列表和子列表的大小。它可以这样工作,但我的方法只循环 N 次,其中 N 是超级列表的长度。【参考方案2】:

如果列表不是太大,最简单的方法是调用Contains

foreach(var item in list1) 
    if (list2.Contains(item) 
        //Do something
    

您可以通过使用自定义IComparer&lt;T&gt; 调用BinarySearch 来加快速度,如下所示:

class MyComparer : IComparer<YourClass> 
    private MyComparer()  
    public static readonly MyComparer Instance = new MyComparer();

    public int CompareTo(YourClass a, YourClass b) 
        //TODO: Handle nulls
        return a.SomeProperty.CompareTo(b.SomeProperty);
    

foreach(var item in list1) 
    if (list2.BinarySearch(item, MyComparer.Instance) >= 0) 
        //Do something
    

在 .Net 3.5 中,您可以使用 HashSet&lt;T&gt; 使其更快:

var hashset = new HashSet<YourClass>(list2);
foreach(var item in list1) 
    if (hashset.Contains(item) 
        //Do something
    

如果您的列表非常大,您应该衡量每个选项的效果并做出相应选择。 否则,请选择第一个选项,这是最简单的。

【讨论】:

【参考方案3】:

您的问题意味着您希望避免每次都遍历第二个列表中的所有项目,这在使用Contains() 的最坏情况的幼稚解决方案中会发生这种情况。由于两个列表都已排序并且list2list1 的子集,因此您知道list1 中的任何条目的索引都不会小于list2 中的相应条目的索引。考虑到这一点,您可以使用两个枚举器制作一个有效的 O(n) 解决方案:

Debug.Assert(list1.Count > 0);
Debug.Assert(list1.Count >= list2.Count);

var enum1 = list1.GetEnumerator();
var enum2 = list2.GetEnumerator();

enum1.MoveNext();
while (enum2.MoveNext())

    // Skip elements from list1 that aren't equal to the current entry in list2
    while (!enum1.Current.Equals(enum2.Current))
        enum1.MoveNext();

    // Fire the OnEqual event for every entry in list1 that's equal to an entry
    // in list2
    do 
        OnEqual(enum1.Current, enum2.Current); 
     while (enum1.MoveNext() && enum1.Current.Equals(enum2.Current));


enum1.Dispose();
enum2.Dispose();

【讨论】:

这就是我想要的!谢谢,伙计!;)【参考方案4】:

如果它们都按唯一属性排序,则可以在迭代期间使用它。这个想法是遍历超集,然后根据该排序的唯一属性推进子集迭代器,直到它与超集匹配或大于/小于(取决于排序顺序)。

对于按升序排序的属性:

if (subsetList.Count > 0)

    using(IEnumerator<T2> subset = subsetList.GetEnumerator())
    
        subset.MoveNext();
        T2 subitem = subsetList.Current;
        foreach(T1 item in supersetList)
        
            while (item.A > subitem.A &&
                   subset.MoveNext())
            
                subitem = subset.Current;
            

            if (item.A == subitem.A)
            
                // Modify item here.
            
        
    

请注意,这实际上并不依赖于 supersetListsubsetList 的超集。 EndangeredMassa 的解决方案更简洁,假设成立。

【讨论】:

这与我的答案相同,只是您不处理超集中的多个条目等于子集中的单个条目的情况。 处理好了。除非超集已超出该项目,否则它不会迭代该子项目。因此,超集中具有相同值的多个条目不会推进子集迭代器。我确实搞砸了while循环的比较。固定。

以上是关于遍历 2 个列表的主要内容,如果未能解决你的问题,请参考以下文章

如何循环遍历列表? [复制]

python 同时遍历俩个列表

在 python 中循环遍历列表的一部分时,我无法正确索引

Python列表边遍历边修改问题解决方案:alist[:]

nextAll() - 多个列表遍历

Python - 遍历没有最后一个元素的列表