遍历 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) 第二个列表是第一个列表的子集。
【讨论】:
起初我以为这是错误的。但是有了sub
是super
的子集的信息,这是我的一个更清洁的解决方案,它只假设排序,因此必须处理跳过错过的匹配。虽然这不能处理具有相同属性值的多个条目。
对。这些假设对于这种方法很重要。
该方法将遍历每个超级列表项的每个子列表项。这意味着它循环 N*M 次,其中 N 和 M 是超级列表和子列表的大小。它可以这样工作,但我的方法只循环 N 次,其中 N 是超级列表的长度。【参考方案2】:
如果列表不是太大,最简单的方法是调用Contains
:
foreach(var item in list1)
if (list2.Contains(item)
//Do something
您可以通过使用自定义IComparer<T>
调用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<T>
使其更快:
var hashset = new HashSet<YourClass>(list2);
foreach(var item in list1)
if (hashset.Contains(item)
//Do something
如果您的列表非常大,您应该衡量每个选项的效果并做出相应选择。 否则,请选择第一个选项,这是最简单的。
【讨论】:
【参考方案3】:您的问题意味着您希望避免每次都遍历第二个列表中的所有项目,这在使用Contains()
的最坏情况的幼稚解决方案中会发生这种情况。由于两个列表都已排序并且list2
是list1
的子集,因此您知道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.
请注意,这实际上并不依赖于 supersetList
是 subsetList
的超集。 EndangeredMassa 的解决方案更简洁,假设成立。
【讨论】:
这与我的答案相同,只是您不处理超集中的多个条目等于子集中的单个条目的情况。 处理好了。除非超集已超出该项目,否则它不会迭代该子项目。因此,超集中具有相同值的多个条目不会推进子集迭代器。我确实搞砸了while循环的比较。固定。以上是关于遍历 2 个列表的主要内容,如果未能解决你的问题,请参考以下文章