Делает ли это то, что вы хотите?
static ILookup<TKey, TValue> Copy<TKey, TValue>(ILookup<TKey, TValue> lookup)
{
return lookup.
SelectMany(g => g,
(g, v) => new KeyValuePair<TKey, TValue>(g.Key, v)).
ToLookup(kvp => kvp.Key, kvp => kvp.Value);
}
Конечно, если вы хотите как-то преобразовать значения, возможно, вы хотите что-то вроде этого:
static ILookup<TKey, TValueOut> Transform<TKey, TValue, TValueOut>(
ILookup<TKey, TValue> lookup,
Func<TValue, TValueOut> selector)
{
return lookup.
SelectMany(g => g,
(g, v) => new KeyValuePair<TKey, TValueOut>(g.Key, selector(v))).
ToLookup(kvp => kvp.Key, kvp => kvp.Value);
}
Обратите внимание, что этот метод содержит промежуточные значения в KeyValuePair
, который, будучи типом значения, хранится в стеке и, следовательно, не требует каких-либо промежуточных выделений памяти. Я профилировал тест, который создает Lookup<int,int>
с 100 ключами, каждый из которых содержит 10 000 элементов (всего 1 000 000).
- Создание
Lookup
делает 1610 выделений.
- Копирование его с помощью моего метода делает 1712 выделений (все выделения, необходимые для его создания, плюс одно для каждого делегата в вызове
SelectMany
и одно для перечислителя для каждого ключа).
- Копирование его с анонимными объектами вместо
KeyValuePair
дает 1 001 712 выделений (все выделения, необходимые для копирования, плюс одно для каждого элемента).
С точки зрения процессора, даже при 100 000 элементов на ключ производительность Lookup
между двумя методами копирования была одинаковой. С 1 000 000 элементов на ключ производительность была разной для двух методов:
- 5,1 сек для создания
- 5,9 с для копирования с
KeyValuePair
- 6,3 сек для копирования с анонимными объектами