Delphi TList<T> Копировать в другой TList?

Я хотел бы знать, есть ли какой-либо безопасный способ скопировать элементы TList в любой другой TList в определенную позицию и с определенной длиной. Должен ли я просто назначить элементы list1 для list2 или есть какая-то функциональность, я не знаю, что обрабатывает это более точно?

Спасибо, что нашли время.

3 ответа

Решение

Если вы намереваетесь ЗАМЕНИТЬ элементы вместо того, чтобы вставлять их в заданную позицию, то ответ заключается в том, что прямого механизма не существует, и итеративное назначение является подходом для использования.

for i := 1 to maxItems do
  dest[ insertPos + i - 1] := src[ i - 1 ];

В этом случае вы должны рассмотреть ситуацию, когда вы добавляете больше элементов, чем в списке мест назначения. Означает ли это, что нужно заменить только то количество элементов, которое "уместится", добавить дополнительные элементы в "освободить место" или не назначить их вообще (если не все), - вопрос, на который могут ответить только ваши требования.

Однако, если вы хотите вставить элементы в список назначения, вы можете использовать комбинацию InsertRange() и Copy() вместе с внутренним массивом , поддерживаемым исходным списком. Например, используя два экземпляра TList :

var
  src, dest: TList<String>;
  insertIndex, maxItems: Integer;

dest.InsertRange( insertIndex, Copy( src.List, 0, maxItems ) );

Для вставки всего списка src вам не нужно использовать Copy(), но вы можете ссылаться на исходный список непосредственно в методе InsertRange():

dest.InsertRange( insertIndex, src );

Примечания по производительности:

Использование Copy() является потенциально дорогой операцией, если список источников велик и / или количество добавляемых подпунктов невелико. Однако фактическая вставка элементов в список адресатов очень эффективна, поскольку метод InsertRange() может освободить место для новых элементов в списке адресатов за одну операцию, а затем вставить новые элементы в пространство, созданное для них. Таким образом, для большего количества добавляемых предметов он все же может оказаться наиболее эффективным.

Альтернативный подход заключается в итеративной вставке исходных элементов по отдельности:

for i := 1 to maxItems do
  dest.Insert( insertIndex, src[i - 1]);

Хотя это позволяет избежать копирования вставляемых элементов массива, итеративная вставка сама по себе потенциально неэффективна, если список адресатов большой и вставляется большее количество элементов, поскольку место для каждого элемента в списке адресатов должно быть выделено отдельно для каждой вставки (хотя потенциальное влияние этого может быть значительно улучшено за счет явного вычисления и предварительного распределения емкости списка адресатов).

например, если вы вставляете 100 элементов из списка 1000 элементов в (точную) середину списка 2000 элементов:

InsertRange( Copy() )       Copy 100 items into an intermediate array
                            Moves 1000 items in the dest list to make room for 2100 (total)
                            Inserts 100 items into the 'blank' space

Iterative insert            100 repetitions of:
                               Move 1000 items in the dest list to make room for 1 more
                               Inserts 1 item

Для вставки 100 элементов InsertRange() потенциально наиболее эффективен. В отличие от этого, если вставить только один элемент из списка источников, то подход InsertRange() влечет за собой чрезмерные издержки.

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

Я бы использовал AddRange для добавления элементов в конец или InsertRange для вставки элементов по определенному индексу.

В дополнение к ответу Энни, Assign копирует из целевого списка (очистка содержимого исходного списка).

list1 := TList.Create();
for val:=1 to 3 do
    list1.Add(Pointer(val));    // list1 contains {1,2,3}

list2 := TList.Create();
for val:=5 to 8 do
    list2.Add(Pointer(val));    // list2 contains {5,6,7,8}

list2.Assign(list1);            // list2 now contains {1,2,3}
list2.Assign(list1);            // list2 still contains {1,2,3}

list2.Free();
list1.Free();

Я предполагаю, что оба списка являются общими TList<T>, И что вы копируете существующие элементы, а не расширяете пункт назначения.

В этом случае, я думаю, for цикл с присваиванием, используя := это путь Поскольку вы имеете дело с универсальным списком, вам нужно использовать оператор, который может работать с любым универсальным типом T, Предполагая, что вам нужно поддержать T это управляемый тип, тогда вы не можете выполнить простую копию памяти. Это оставляет вас с заданием.

Другие вопросы по тегам