在C#.NET中将两个(或多个)列表合并为一个


245

是否可以使用C#在.NET中将两个或多个列表转换为一个列表?

例如,

public static List<Product> GetAllProducts(int categoryId){ .... }
.
.
.
var productCollection1 = GetAllProducts(CategoryId1);
var productCollection2 = GetAllProducts(CategoryId2);
var productCollection3 = GetAllProducts(CategoryId3);

是否要合并productCollection1 2和3?

您是说要在一个列表中合并多个列表?
Amr Badawy

您的示例使我感到困惑...您在寻找AddRange-Method吗?
鲍比2010年

Answers:


456

您可以使用LINQ ConcatToList方法:

var allProducts = productCollection1.Concat(productCollection2)
                                    .Concat(productCollection3)
                                    .ToList();

请注意,有更有效的方法可以执行此操作-上面的方法基本上会遍历所有条目,从而创建动态大小的缓冲区。正如您可以预测的开始大小一样,您不需要这种动态大小调整...因此您可以使用:

var allProducts = new List<Product>(productCollection1.Count +
                                    productCollection2.Count +
                                    productCollection3.Count);
allProducts.AddRange(productCollection1);
allProducts.AddRange(productCollection2);
allProducts.AddRange(productCollection3);

(为了提高效率AddRange而特例ICollection<T>。)

除非您确实需要,否则我不会采用这种方法。


4
顺便说一句,productCollection1.ForEach(p => allProducts.Add(p))比AddRange更具性能。
Marc Climent,2013年

8
@MarcCliment:这是一个相当大胆的概括性声明-尤其是AddRange要进行从一个基础数组到另一个基础数组的块复制时。您是否有任何链接可以证明这一点?
乔恩·斯基特

4
@MarcCliment:一方面,在您的测试中,您并未创建具有正确最终尺寸的列表-而我的答案中的代码却确实如此。这样做,至少使用AddRange可以更快地进行10000 * 10000测试-尽管其他结果不一致。(您还应该在测试之间强制进行垃圾收集-并且我认为非常短的测试无意义地小。)
Jon Skeet

5
@MarcCliment:哪种情况?哪个CLR?哪种CPU架构?在我的机器上,我得到了混合的结果。这就是为什么我不喜欢声明/接受诸如“ BTW,productionCollection1.ForEach(...)比AddRange性能更高”之类的笼统声明。性能很少这么容易描述。我惊讶,尽管AddRange并没有轻易击败它-我需要进一步调查。
乔恩·斯基特

14
我已经更新了要点(gist.github.com/mcliment/4690433),并使用BenchmarksDotNet进行了性能测试,并进行了适当的测试,这AddRange是原始速度的最佳选择(大约4倍,列表越大,增加越好),因为乔恩建议。
马克·克莱门特

41

假设您想要一个包含指定类别ID的所有产品的列表,则可以将查询视为投影,然后进行展操作。还有,做一个LINQ经营者:SelectMany

// implicitly List<Product>
var products = new[] { CategoryId1, CategoryId2, CategoryId3 }
                     .SelectMany(id => GetAllProducts(id))
                     .ToList();

在C#4中,可以将SelectMany缩短为: .SelectMany(GetAllProducts)

如果您已经有了代表每个ID的产品的列表,那么您需要的是串联,正如其他人指出的那样。


8
如果OP出于任何其他原因不需要单独的列表,那么这是一个很好的解决方案。
乔恩·斯基特

2
当我发现这个问题时,遇到了类似的问题,打算放弃并提出一个问题。这是最好的答案。尤其是如果代码已经在列表或数组中包含了这些类别,就我而言就是如此。

22

您可以使用LINQ将它们结合起来:

  list = list1.Concat(list2).Concat(list3).ToList();

不过,较传统的使用方法List.AddRange()可能会更有效。





4

我知道这是一个老问题,我想我可以再加上2美分。

如果您有List<Something>[],可以使用Aggregate

public List<TType> Concat<TType>(params List<TType>[] lists)
{
    var result = lists.Aggregate(new List<TType>(), (x, y) => x.Concat(y).ToList());

    return result;
}

希望这可以帮助。


2

我已经对此发表了评论,但我仍然认为这是一个有效的选择,只需测试一下在您的环境中是否是更好的一种解决方案。在我的特殊情况下,使用source.ForEach(p => dest.Add(p))效果要好于经典,AddRange但我还没有研究为什么在低级别使用。

您可以在此处查看示例代码:https : //gist.github.com/mcliment/4690433

因此,选项为:

var allProducts = new List<Product>(productCollection1.Count +
                                    productCollection2.Count +
                                    productCollection3.Count);

productCollection1.ForEach(p => allProducts.Add(p));
productCollection2.ForEach(p => allProducts.Add(p));
productCollection3.ForEach(p => allProducts.Add(p));

测试它是否适合您。

免责声明:我不主张这种解决方案,我找到Concat了最明确的解决方案。我刚刚说过-在与Jon的讨论中-在我的机器上,此案例的性能优于AddRange,但他说,我的知识比我多得多,这是没有道理的。如果要比较的话,有要点。


无论您在评论中与乔恩·斯凯特(Jon Skeet)进行辩论,我都更喜欢他提出的解决方案所提供的简洁性,这只是增加了对代码意图的不必要解释。
Vix

1
我认为最明确的选择是Concat作为AddRange的泛化,在语义上更正确,并且不会就地修改列表,从而使其可链接。与乔恩的讨论是关于性能,而不是清洁度。
马克·克莱门特

我有这样的东西:var allProducts = lstName.Concat(lstCMSID) .Concat(lstSpecialtyPhys) .ToList();它添加到GridView但作为一列。我想将它们分为三个单独的列。
2015年

2
// I would make it a little bit more simple

 var products = new List<List<product>> {item1, item2, item3 }.SelectMany(id => id).ToList();

这样,它是一个多维列表,.SelectMany()会将其展平为产品的IEnumerable,然后在之后使用.ToList()方法。


2

将列表合并或合并到一个列表中。

  • 必须做一件事:两个列表的类型将相等。

  • 例如:如果我们有list,string那么我们可以添加另一个列表到现有列表中,列表的类型为string,否则我们不能。

范例

class Program
{
   static void Main(string[] args)
   {
      List<string> CustomerList_One = new List<string> 
      {
         "James",
         "Scott",
         "Mark",
         "John",
         "Sara",
         "Mary",
         "William",
         "Broad",
         "Ben",
         "Rich",
         "Hack",
         "Bob"
      };

      List<string> CustomerList_Two = new List<string> 
      {
         "Perter",
         "Parker",
         "Bond",
         "been",
         "Bilbo",
         "Cooper"
      };

      // Adding all contents of CustomerList_Two to CustomerList_One.
      CustomerList_One.AddRange(CustomerList_Two);

      // Creating another Listlist and assigning all Contents of CustomerList_One.
      List<string> AllCustomers = new List<string>();

      foreach (var item in CustomerList_One)
      {
         AllCustomers.Add(item);
      }

      // Removing CustomerList_One & CustomerList_Two.
      CustomerList_One = null;

      CustomerList_Two = null;
      // CustomerList_One & CustomerList_Two -- (Garbage Collected)
      GC.Collect();

      Console.WriteLine("Total No. of Customers : " +  AllCustomers.Count());
      Console.WriteLine("-------------------------------------------------");
      foreach (var customer in AllCustomers)
      {
         Console.WriteLine("Customer : " + customer);
      }
      Console.WriteLine("-------------------------------------------------");

   }
}

1

在特殊情况下:“ List1的所有元素都转到新的List2” :(例如,字符串列表)

List<string> list2 = new List<string>(list1);

在这种情况下,将使用list1中的所有元素生成list2



0

当列表很少时,但您不知道确切多少,请使用以下命令:

listsOfProducts 包含一些充满对象的列表。

List<Product> productListMerged = new List<Product>();

listsOfProducts.ForEach(q => q.ForEach(e => productListMerged.Add(e)));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.