我有一个List<T1>
项目和第二个项目List<T2>
。这两个列表按属性A按字母顺序排序。我知道List<T2>
中的项目列表是List<T1>
的子集,并且中不存在List<T1>
中不存在的项目。遍历2个列表
我需要迭代List<T1>
并在每次匹配变量List<T2>
时更改变量。什么是最快和最好的方式来做到这一点?我假设我需要遍历这两个列表,但我知道做一个嵌套的foreach是没有意义的。
我有一个List<T1>
项目和第二个项目List<T2>
。这两个列表按属性A按字母顺序排序。我知道List<T2>
中的项目列表是List<T1>
的子集,并且中不存在List<T1>
中不存在的项目。遍历2个列表
我需要迭代List<T1>
并在每次匹配变量List<T2>
时更改变量。什么是最快和最好的方式来做到这一点?我假设我需要遍历这两个列表,但我知道做一个嵌套的foreach是没有意义的。
对于这种类型的东西,我更喜欢双重循环。看下面的例子。
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”的一个子集,这是一个比我更清洁的解决方案,它只是假设排序,因此必须处理跳过错过的匹配。虽然这不处理具有相同属性值的多个条目。 – jdmichal 2010-07-20 17:36:16
对。这些假设对于这种方法很重要。 – EndangeredMassa 2010-07-20 18:49:25
该方法将遍历每个超级列表项目的每个子列表项目。这意味着它循环N * M次,其中N和M是超级列表和子列表的大小。它可以这样工作,但我的方法只循环N次,其中N是超级列表的长度。 – EndangeredMassa 2010-07-20 19:42:52
如果名单是不是太大,您这样做最简单的方法是调用Contains
:
foreach(var item in list1) {
if (list2.Contains(item) {
//Do something
}
}
你可以使其更快通过使用自定义IComparer<T>
调用BinarySearch
,像这样:
var hashset = new HashSet<YourClass>(list2);
foreach(var item in list1) {
if (hashset.Contains(item) {
//Do something
}
}
:
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>
使其更快
如果您的列表非常大,您应该测量每个选项的性能并进行相应选择。
否则,请选择其中一个最简单的选项。
如果它们都在唯一属性上排序,则可以在迭代过程中使用它。这个想法是循环遍历超集,然后基于排序后的唯一属性推进子集迭代器,直到它匹配或者更大/更小(取决于排序顺序)而不是超集。
对于升序排序属性:
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的解决方案更为简洁。
这与我的回答相同,只是您不处理超集中有多个条目等于子集中的单个条目的情况。 – 2010-07-20 17:29:11
这是处理。除非超集超出该项目,否则它不会迭代子项。因此,超集中相同值的多个条目不会推进子集迭代器。尽管我在while循环中做了比较。固定。 – jdmichal 2010-07-20 17:31:34
您的问题意味着您要避免每次都迭代第二个列表中的所有项目,这是在使用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();
这就是我一直在寻找的! Thx,mate !;) – user1859587 2013-01-23 13:52:10
是相同类型的列表吗? – SLaks 2010-07-20 17:12:51
列表多久?如果我们在谈论微小的数字,不要排除一些非常简单的O(n^2)原油解决方案。 – 2010-07-20 17:31:42
'从List1中的x连接y在x.P中的List2中等于y.P'? – Gabe 2010-07-20 17:50:37