问题 查找所有相交数据,而不仅仅是唯一值


我以为我明白了 Intersect但事实证明我错了。

 List<int> list1 = new List<int>() { 1, 2, 3, 2, 3};
 List<int> list2 = new List<int>() { 2, 3, 4, 3, 4};

 list1.Intersect(list2) =>      2,3

 //But what I want is:
 // =>  2,3,2,3,2,3,3

我可以想象一下:

 var intersected = list1.Intersect(list2);
 var list3 = new List<int>();
 list3.AddRange(list1.Where(I => intersected.Contains(I)));
 list3.AddRange(list2.Where(I => intersected.Contains(I)));

在LINQ中有更简单的方法来实现这一目标吗?

我需要说明我不关心结果的顺序。

2,2,2,3,3,3,3也可以。

问题是我在一个非常大的集合上使用它,所以我需要效率。

我们谈论的是对象,而不是整体。这只是一个简单的例子,但我意识到这可以有所作为。


12256
2018-02-01 21:00


起源

鉴于您的更新,可能有更有效的方法来解决您的问题。告诉我们有关数据的更多信息。具体来说,我感兴趣的是你的大集合是否主要是独特的元素,或者大多数是重复的。我也有兴趣知道元素是否真的是整数,或者这是否是一个更复杂类型的替代;具体来说,有没有 总订货量 在您的数据上定义?也就是说,给定一组这样的数据,是否有一个独特的,定义明确的从最小到最大的排序? - Eric Lippert


答案:


让我们看看我们是否可以精确地描述你想要的东西。如果我错了,请纠正我。你想要:列表1的所有元素,按顺序,也出现在列表2中,然后是列表2的所有元素,按顺序,也出现在列表1中。是吗?

似乎很简单。

return list1.Where(x=>list2.Contains(x))
     .Concat(list2.Where(y=>list1.Contains(y)))
     .ToList();

请注意,这是 没有效率 对于大型列表。如果列表每个都有一千个项目,那么这将进行几百万次比较。如果您处于这种情况,那么您希望使用更有效的数据结构来测试成员身份:

list1set = new HashSet(list1);
list2set = new HashSet(list2);

return list1.Where(x=>list2set.Contains(x))
     .Concat(list2.Where(y=>list1set.Contains(y)))
     .ToList();

它只进行了几千次比较,但可能会使用更多的内存。


15
2018-02-01 21:22



您的LINQ查询不会提供与其他两个查询相同的结果 - 如果元素e在list1中出现n次,而在list2中出现m,则它们包含n * m次,这不是所需的行为。 - kvb
很棒的捕获 @kvb。我完全错过了,因为在给定的例子中,它们碰巧看起来容易混淆。我将删除不正确的代码。谢谢! - Eric Lippert
关于HashSet的有趣之处。我不知道它更有效率。会调查一下! - Peterdk
@Peterdk:列表是O(n)来测试元素的成员资格,但作为交换,你可以(1)维护一个排序,(2)有重复。 HashSet是O(1)来测试元素的成员资格但不保持元素的顺序,并且从不包含重复项。如果你愿意加倍记忆并同时使用HashSet 和 列表,您可以充分利用这两个世界。 - Eric Lippert


var set = new HashSet(list1.Intersect(list2));
return list1.Concat(list2).Where(i=>set.Contains(i));

1
2018-02-01 22:00





我不相信内置API可以实现这一点。但您可以使用以下内容来获得您正在寻找的结果。

IEnumerable<T> Intersect2<T>(this IEnumerable<T> left, IEnumerable<T> right) {
  var map = left.ToDictionary(x => x, y => false);
  foreach ( var item in right ) {
    if (map.ContainsKey(item) ) {
      map[item] = true;
    }
  }
  foreach ( var cur in left.Concat(right) ) {
    if ( map.ContainsKey(cur) ) {
      yield return cur;
    }
  }
}

-1
2018-02-01 21:19