【问题标题】:Iterate through 2 Lists遍历 2 个列表
【发布时间】: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?
  • 几百,但它正在进入一个网页。此外,这些列表是不同的类型,对可比较的变量具有相同的属性/方法调用。

标签: c# .net .net-3.5 c#-3.0


【解决方案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循环的比较。固定。
    猜你喜欢
    • 2021-04-13
    • 1970-01-01
    • 2014-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-12
    • 2011-04-26
    相关资源
    最近更新 更多