Дело тут не в скорости, а в затрате памяти. Самая быстрая конструкция - чистый массив. Всё остальное будет уступать по производительности. Листы и словари - это лишь высокоуровневые оболочки обычного массива, предоставляющие всевозможный сахар.
Да, но не недооценивайте их. Списки хранят у себя массив большего чем надо размера, с запасом, чтоб позволяет быстро добавлять элементы. И при этом не надо выделять каждый раз новую память, чтоб изменить количество элементов, это самое важное, почему я их рекомендую.
И связные списки, кстати, не основаны на массиве. И скорость добавления элементов у них больше чем у чего либо другого, а памяти выделяется лишь чуть больше чем надо и только когда надо.
так это выходит каждый раз когда нужно изменить исходный массив массивов придется создавать дополнительный массив массивов подходящей длины и туда копировать ссылки, а после этого копировать ссылку на него в исходный? И в чем тут профит, если все равно нужна еще одна память под дополнительный массив?
до связных списков я еще не добрался, а вот насчет реализовать прямоугольный списо на матрице… я даже не представляю как это выглядит, не говоря уже за реализацию =)
Осторожно: очень много костылей! Это лишь чтоб показать идею.
type
RectList<T> = class
private mtx:array[,] of T;
private l1,l2:integer;
private function GetItem(i1,i2:integer):T;
begin
if cardinal(i1) > cardinal(l1) then raise new System.IndexOutOfRangeException('i1 неправильный') else
if cardinal(i2) > cardinal(l2) then raise new System.IndexOutOfRangeException('i2 неправильный') else
Result := mtx[i1,i2];
end;
private procedure SetItem(i1,i2:integer; o:T) :=
if cardinal(i1) > cardinal(l1) then raise new System.IndexOutOfRangeException('i1 неправильный') else
if cardinal(i2) > cardinal(l2) then raise new System.IndexOutOfRangeException('i2 неправильный') else
mtx[i1,i2] := o;
public constructor;
begin
mtx := new T[4,4];
end;
public procedure Resize(nl1,nl2:integer);
begin
var res := new T[nl1,nl2];
//ToDo реализовать через System.Array.Copy, так быстрее
//Только копировать придётся построчно, иначе не сработает
for var i1 := 0 to Min(Cap1,nl1)-1 do
for var i2 := 0 to Min(Cap2,nl2)-1 do
res[i1,i2] := mtx[i1,i2];
mtx := res;
end;
public property Length1:integer read l1;
public property Length2:integer read l2;
public property Cap1:integer read mtx.GetLength(0) write Resize(value,Cap2);
public property Cap2:integer read mtx.GetLength(1) write Resize(Cap1,value);
public property Item[i1,i2:integer]:T read GetItem write SetItem; default;
public procedure Add1(o:ICollection<T>; at:integer);
begin
if cardinal(at) > cardinal(l1) then raise new System.IndexOutOfRangeException('at неправильный');
var ol := o.Count;
if ol > l2 then raise new System.IndexOutOfRangeException('o.Count неправильный');
if l1 = Cap1 then Resize(Cap1*2, Cap2);
//ToDo опять же, через Array.Copy надо
for var i1 := l1-1 downto at do
for var i2 := 0 to l2-1 do
mtx[i1+1,i2] := mtx[i1,i2];
var i2 := 0;
foreach var a in o do
begin
mtx[at, i2] := a;
i2 += 1;
end;
l1 += 1;
end;
public procedure Add2(o:ICollection<T>; at:integer);
begin
if cardinal(at) > cardinal(l2) then raise new System.IndexOutOfRangeException('at неправильный');
var ol := o.Count;
if ol > l1 then raise new System.IndexOutOfRangeException('o.Count неправильный');
if l2 = Cap2 then Resize(Cap1, Cap2*2);
//ToDo опять же, через Array.Copy надо
for var i2 := l2-1 downto at do
for var i1 := 0 to l1-1 do
mtx[i1,i2+1] := mtx[i1,i2];
var i1 := 0;
foreach var a in o do
begin
mtx[i1, at] := a;
i1 += 1;
end;
l2 += 1;
end;
public procedure Remove1(at:integer);
begin
if cardinal(at) > cardinal(l1) then raise new System.IndexOutOfRangeException('at неправильный');
//ToDo опять же, через Array.Copy надо
for var i1 := at to l1-1 do
for var i2 := 0 to l2-1 do
mtx[i1,i2] := mtx[i1+1,i2];
l1 -= 1;
end;
public procedure Remove2(at:integer);
begin
if cardinal(at) > cardinal(l2) then raise new System.IndexOutOfRangeException('at неправильный');
//ToDo опять же, через Array.Copy надо
for var i2 := at to l2-1 do
for var i1 := 0 to l1-1 do
mtx[i1,i2] := mtx[i1,i2+1];
l2 -= 1;
end;
class function operator implicit(a: RectList<T>): array[,] of T;
begin
Result := new T[a.l1,a.l2];
//ToDo опять же, через Array.Copy надо
for var i1 := 0 to a.l1-1 do
for var i2 := 0 to a.l2-1 do
Result[i1,i2] := a.mtx[i1,i2];
end;
end;
begin
var mtx := new RectList<byte>;
mtx.Resize(3,3);
mtx.l2 := 3;//так вообще не даст сделать в нормальном коде, надо как то лучше реализовать.
mtx.Add1(new byte[](10,11,12),0);
mtx.Add1(new byte[](13,14,15),1);
mtx.Add1(new byte[](16,17,18),2);
var res: array[,] of byte := mtx;
res.Println;
writeln;
mtx.Add1(new byte[](21,22,23),1);
mtx.Add2(new byte[](31,32,33,34),1);
res := mtx;
res.Println;
end.
Примерно так же реализован обычный список.
А связный список - этот тот, в котором после каждого элемента идёт ссылка на следующий. Таким образом прямо в его центр можно вставить что то, без особых проблем. Хотя, конечно, получение элемента по индексу - ужасно медленное, из за того, сколько указателей придётся разыменовывать. Он полезен только если надо вставить много элементов в разные места. Тогда надо сначала преобразовать массив к связному списку, потому добавить/убрать всё что надо, а потом преобразовать назад в массив.
Если неэффективность процессорного времени можно стерпеть, то неправильное использование памяти - катастрофа!
Хотя необоснованные огрехи логики/производительности/памяти свидетельствуют о неверной идее, небрежности или отсутствии знаний, что нужно не терпеть, а исправлять, однако обоснованное (осознанное) пожертвование одним ради другого (например, памятью ради простоты структуры) – это основа современного проектного ПО, где балансируют рисками и компромиссами:
- что будет если не хватит памяти? - не запустятся другие программы;
- а что будет если ошибиться в более сложной структуре ради последнего выжатого байта? - нет смысла в такой программе.
Как-то попадалась реализации “заархивированной” структуры, может и в современном многопоточном терабайтном мире .NET есть подобные ноу-хау.
Именно это я имею в виду. Я всегда делаю оптимизацию именно по памяти, за редким исключением, когда несколько байт даюсь жёсткое замедление программы.
Ну так я же сказал, что мне это напомнило фразу, а не является тем же самым.
Списки предназначены для сложных операций с массивами. Человек хочет всего лишь удалять строки из матриц. Это можно сделать гораздо быстрее с помощью простых массивов, при необходимости подкрепив это указателями.
Нет, говорили про оба, и столбцы и строки
А разве не по производительности? Тогда почему вы это называли преимуществом:
Это ведь не даст улучшения на фронте памяти, это лишь увеличит производительность.
Ну всё равно, если нужно преимущество памяти - надо тогда как раз создавать новую матрицу, и в новую копировать то что было. Потому что старую после этого удалит, а значит лишней памяти не надо. А array of array
создаёт больше ссылок и заголовков каждого массива, поэтому на него надо больше памяти.
Вообще, если вам надо сэкономить максимум памяти - выделяйте область в неуправляемой памяти, с помощью System.Runtime.InteropServices
, а array of array
вам ничем не поможет.
Более того, следующее:
Не значит что проблема в памяти. выделение больших объёмов памяти так же бьёт и по производительности.
Не только, не только строки, и столбцы, и не только удалять, но и добавлять =). Хорошо, со строками разобрались вроде, ну так. И со столбцами такое тоже можно наверное замутить, отдельно. А если нужно в одной задаче удалять (добавлять) и строки и столбцы, тогда как?
Точно так же.
напишите код пожалуйста
Uses System;
Procedure SetLength<T>(var arr: array of T, length: Int32);
Begin
arr := new T[length];
End;
Begin
var a: array of array of Int32;
SetLength(a, 10); // 10 строк
For Var i := 0 to a.Length - 1 do
a[i] := new Int32[9]; // 10 столбцов
var newA: array of array of Int32;
SetLength(newA, 9); // 11 строк. Последнюю надо подписать.
newA[0] := a[0];
newA[1] := a[1];
newA[2] := a[2];
newA[3] := a[3];
newA[4] := a[4];
newA[5] := a[5];
newA[6] := a[6];
newA[7] := a[7];
newA[8] := a[8];
newA[9] := a[9];
newA[10] := строка;
a := newA;
End.
Как в TabControl помещая вкладки слева (Alignment: Left), получить название вкладок горизонтально – с заглавием слева-на-право, а не снизу-вверх? И, насколько я понял, в WindowForms непросто раскрасить вкладки, только через OwnerDrawFixed?
Также где-то потерял пример фоновой записи (с возможностью отмены) в текстовый файл.
Благодарю
Это вообще из System.Windows.Forms
, или откуда? Приведите пример кода, что где как алайнится, и где в не таком порядке имена даёт.
Да, WindowsForms, и даже без кода – прямо с дизайнера проекта: если табы-заголовки выровнять по левому краю, они автоматически пишутся снизу-вверх (вертикально) и пока не знаю как победить, чтобы выглядело на подобии wizard’а (слева-направо) горизонтально.
Я всё равно вас не понимаю, приведите примеры. И что такое этот ваш визард?
Хотя, я лично порекомендовал бы дизайнер не использовать, разработчики сами сказали что не поддерживают. Там слишком много глюков.
А какая альтернатива? Для многих написание UI - острый нож.
На сколько я знаю - их, к сожалению, нет. Только руками всё делать.
Хотя, конечно, есть ещё вариант - в дизайнере размещать, чтоб получить необходимые смещения и размеры. А потом скопировать их в программу без дизайнера. Не очень удобно, но, по моему, то что что не делай - обязательно наткнёшься сразу на несколько багов, это ещё менее удобно.
Вот. Но что-то лучше, чем ничего.