2010-03-22 2 views
8

У меня есть 3 (Edit) взаимно исключающий IEnumerables, который я хочу перебрать. Я хочу сделать что-то вроде этого:Обо мне по коллекции IEnumerables

IEnumerable<Car> redCars = GetRedCars(); 
IEnumerable<Car> greenCars = GetGreenCars(); 
IEnumerable<Car> blueCars = GetBlueCars(); 

foreach(Car c in (redCars + greenCars + blueCars)) { 
    c.DoSomething(); 
} 

... 

Лучшего способом я могу думать:

... 
List<Car> allCars = new List(); 
allCars.AddRange(redCars); 
allCars.AddRange(greenCars); 
allCars.AddRange(blueCars); 
foreach(car in allCars) { 
    ... 
} 
... 

Есть более краткий способ сделать это? Похоже, что сближение IEnumberables должно быть тривиальным.

ответ

20

С LINQ:

foreach(car in redCars.Concat(greenCars).Concat(blueCars)) { 
    //... 
} 

Для получения информации, разница здесь между Union и Concat что Union будет делать дополнительную работу, чтобы гарантировать уникальность; так что если вы не ожидаете дубликатов (или, альтернативно: не обращайте на них внимание), то Concat быстрее.

+2

Хех ... Я начал с Конката, а затем пошел в Союз, а потом решил, что не могу решить, что использовать, поэтому я удалил свой ответ. Мне нравится, что вы все объясняете разницу. +1 – Randolpho

+0

Именно то, что я искал. Благодаря! – sdr

+0

@sdr: Обратите внимание, что здесь есть компромисс между временем и пространством. Предположим, у вас было не три, а двадцать таких списков, каждая из которых насчитывала десять тысяч элементов. Ваш метод (объединить их все вместе в один большой список) занимает около 20 x 10K дополнительных ссылок и выполняет 20 x 10K операций копирования ссылок. С другой стороны, выполнение 20 наивных конкат, принимает нулевые дополнительные ссылки, но вложенные конкаты имеют нагрузку на копирование, основанную на их глубине вложенности, поэтому будет 10 x 11 x 10K = 110K копий, а не 20K копий. Подробнее см. Http://blogs.msdn.com/wesdyer/archive/2007/03/23/all-about-iterators.aspx. –

0

Используйте метод LINQ Union.

foreach(Car c in redCars.Union(greenCars).Union(blueCars)) 
{ 
    c.DoSomething(); 
} 
+0

Если вам абсолютно необходимо сделать это за один цикл, я бы пошел с LINQ. –

+0

Не думал об этом. Но разве это не будет сложным, поскольку это должно их сравнить? Тем более, что три группы являются взаимоисключающими. – sdr

+1

Для информации «Union» будет стоить дороже, чем «Concat» здесь, и вполне может произвести отличный результат для подхода «AddRange» (который по сути является «Concat»). –

0

Есть ли причина, по которой вам нужно сделать это в одном цикле? Если это так, второй вариант, вероятно, лучший. Нет ничего, что будет работать, как первый путь.

Если нет необходимости делать это в одном цикле, я бы сделал это в трех циклах, которые были бы более эффективными по времени.

+1

Я думаю, что «более эффективное время» является большим требованием. Также обратите внимание, что создание списка (с распределением, копированием и т. Д.) Также имеет накладные расходы. И там очень много * - это что-то, что будет работать как первый способ. –

+0

@ Scott, если он работает с каждым автомобилем одинаково, зачем ему больше одного цикла? Это просто избыточный код. –

3

Как указано в других ответах, Union делает дополнительную работу по устранению дубликатов, Concat просто объединяет последовательности. Однако, как я заметил в комментарии выше, затраты на производительность для глубоко вложенных конкат. Можно рассмотреть также с помощью SelectMany выравниваться кучу итераторов:

var carLists = new[]{GetRedCars(), GetGreenCars(), GetBlueCars()}; 
var allCars = from carList in carLists 
       from car in carList 
       select car; 
foreach(var c in allCars) { ... } 

Вы можете обнаружить, что, чтобы быть более гибкими, если вы обнаружите, что вы на самом деле намного больше, чем три списки перебирать.

Смежные вопросы