Как предварительно выделить C# Список списка Т - PullRequest
0 голосов
/ 08 января 2020

Я бы хотел предварительно выделить List<List<double>>. Я знаю, что могу предварительно выделить 1-D списки следующим образом:

List<double> MyList = new List<double>(SomeSize);

Возможно ли это сделать для вложенных списков? В C ++ я бы сделал:

vector<vector<double>> MyList(OuterSize, vector<double>(InnerSize));

Не могу найти эффективного способа сделать это в C#, но я уверен, что есть способ ...

Ответы [ 3 ]

3 голосов
/ 08 января 2020

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

Список на самом деле не сравним с массивом C ++. Вы выделяете для него память, это означает, что вы избегаете увеличения списка, но список начинается с Count = 0, и вам нужно добавить элементы, вы не можете получить к ним доступ с помощью индексатора. И инициализация с емкостью необязательна! Это немного увеличивает производительность, если вы заранее знаете свою конечную длину.

Если вы разбираетесь с массивом фиксированной длины, вы можете выделить многомерный массив, например

    int[,] x = new int[4,5];

сразу.

2 голосов
/ 08 января 2020

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

Вы можете использовать

var list = Enumerable.Range(1, OuterSize)
     .Select( x => new List<T>(InnerSize) )
     .ToList();

При этом создается список с Outersize элементами, каждый из которых содержит пустой список, который был предварительно выделен для содержания InnerSize элементов.

Однако при таком подходе вам все равно придется добавлять элементы во внутренние списки. , Если вы хотите sh использовать ElementAt немедленно, это не сработает. Вы должны предварительно заполнить внутренние списки. Вы можете использовать:

var list = Enumerable.Range(1, OuterSize)
    .Select
    (
        x => Enumerable.Range(1, InnerSize)
                 .Select( y => default(T) )
                 .ToList()
    )
    .ToList();

Это создает список OuterSize элементов, каждый из которых представляет собой список InnerSize элементов (каждый из которых имеет одинаковое значение null / значение по умолчанию).

0 голосов
/ 08 января 2020

В вашем коде C ++ используется «конструктор заполнения» vector, который создает все элементы первого измерения коллекции со списками, представляющими второе измерение.

Для * не существует конструктора заполнения 1036 * List<T> объектов, что позволяет указать начальную емкость. Существующий конструктор заполнения гарантирует, что список будет иметь «достаточную» емкость для элементов, предоставленных параметром IEnumerable<T>, но он не гарантирует, что емкость будет точно соответствовать количеству перечислимых параметров (частично потому, что перечислимые, по замыслу, не раскрывайте их мощность, поэтому единственный способ точно сопоставить емкость - изменить размер базового массива списка по одному элементу за раз.

Вы можете сделать это в две строки с небольшим Linq, три с традиционный l oop, путем создания пустого списка требуемой емкости, а затем добавления объектов второго измерения, каждый из которых инициализируется в желаемую емкость:

var myList = new List<List<T>>(5);
//Option A: Linq
myList.AddRange(Enumerable.Repeat(0, 5).Select(x => new List<string>(4)));
//Option B: Loop
for(i=0;i<5;i++)
    myList.Add(new List<string>(4));

Ваш C ++, на некотором уровне, будет делать что-то похожее на одно из них, в C# просто нет ничего, чтобы абстрагировать это за конструктором.


Если разбить первый параметр, метод List<T>.AddRange() добавляет каждый элемент IEnumerable<T> к списку, изменяя размеры по мере необходимости (здесь не нужно, когда мы добавляем до указанной емкости). Поэтому нам нужно сгенерировать перечислимую последовательность списков. Ну, для этого есть метод Linq. Метод Enumerable.Repeat() генерирует перечисляемую последовательность, которая повторяет указанное значение указанное количество раз. Однако если бы мы указали «new List ()» в качестве значения для повторения, этот конструктор будет выполняться только один раз (до вызова Repeat() для оценки передаваемого значения параметра), и мы получим ту же ссылку на один List повторяется четыре раза. Вместо этого мы хотим создать четыре списка, поэтому нам нужно определить этот конструктор как строку повторяемого кода, то есть нам нужен делегат, который мы можем выполнить как вызов метода.

Без перегрузки Enumerable.Repeat() принимает Func<T> и возвращает IEnumerable<T>; если вы хотите повторить функцию с этим методом, вы получите IEnumerable<Func<T>>. Но функция Select<T>() может быть вызвана для перечислимой последовательности входных данных и создает перечислимую последовательность всех результатов лямбда-оператора (форма определения метода анонимного делегата), учитывая каждый элемент входных данных, перечисляемый в качестве параметра (который мы игнорируем, потому что это мусор; все, что нам нужно для перечисляемого ввода, это то, что он имеет 5 элементов). Итак, теперь у нас есть Enumerable<List<string>>, который родительский List будет отбрасывать во внутреннюю коллекцию.

Да, я мог бы быть умнее и сделать что-то вроде:

myList.AddRange(Enumerable.Repeat(()=>new List<string>(4), 5).Select(x => x()));

Это тоже самое Дело в том, что только то, что повторяет метод Repeat(), не является данными мусора, это ссылка на лямбду, выполняющую конструкцию объекта, которая затем вызывается в лямбде метода Select(). Но теперь у меня есть две лямбда-выражения, которые реализованы в виде частных формульных функций класса, содержащего этот код, что добавляет к полученному объекту больше беспорядка, чем нам действительно нужно. Компилятор также не может определить тип вывода generi c для функции Repeat() из оператора lambda, поэтому приведенный выше код не компилируется; Мне бы пришлось явно указать тип generi c, и эта строка кода (и C# как язык) достаточно многословна, как есть.

...