Лекция 9 Список переменного размера Оператор Visual Basic ReDim позволяет изменять размер массива. Вы можете использовать это свойство для построения простого списка переменного размера. Начните с объявления безразмерного массива для хранения элементов списка. Также определите переменную NumInList для отслеживания числа элементов в списке. При добавлении элементов к списку используйте оператор ReDim для увеличения размера массива, чтобы новый элемент мог поместиться в нем. При удалении элемента также используйте оператор ReDim для уменьшения массива и освобождения ненужной больше памяти. Dim List() As String Dim NumInList As Integer ‘ Список элементов. ‘ Число элементов в списке. Sub AddToList(value As String) ‘ Увеличить размер массива. NumInList = NumInList + 1 ReDim Preserve List (1 To NumInList) ‘ Добавить новый элемент к концу списка. List(NumInList) = value End Sub Sub RemoveFromList() ‘ Уменьшить размер массива, освобождая память. NumInList = NumInList – 1 ReDim Preserve List (1 To NumInList) End Sub Эта простая схема неплохо работает для небольших списков, но у нее есть пара недостатков. Во-первых, приходится часто менять размер массива. Для создания списка из 1000 элементов, придется 1000 раз изменять размер массива. Хуже того, при увеличении размера списка, на изменение его размера потребуется больше времени, поскольку придется каждый раз копировать растущий список в памяти. Для уменьшения частоты изменений размера массива, можно добавлять дополнительные элементы к массиву при увеличении его размера, например, по 10 элементов вместо одного. При этом, когда вы будете добавлять новые элементы к списку в будущем, массив уже будет содержать неиспользуемые ячейки, в которые вы сможете поместить новые элементы без увеличения размера массива. Новое увеличение размера массива потребуется, только когда пустые ячейки закончатся. Подобным же образом можно избежать изменения размера массива при каждом удалении элемента из списка. Можно подождать, пока в массиве не накопится 20 неиспользуемых ячеек, прежде чем уменьшать его размер. При этом нужно оставить 10 свободных ячеек для того, чтобы можно было добавлять новые элементы без необходимости снова увеличивать размер массива. Заметим, что максимальное число неиспользуемых ячеек (20) должно быть больше, чем минимальное число (10). Это уменьшает число изменений размера массива при удалении или добавлении его элементов. При такой схеме в списке обычно есть несколько свободных ячеек, тем не менее их число достаточно мало, и лишние затраты памяти невелики. Свободные ячейки гарантируют возможность добавления или удаления элементов без изменения размера массива. Фактически, если вы неоднократно добавляете к списку, а затем удаляете из него один или два элемента, вам может никогда не понадобиться изменять размер массива. Dim List() As String Dim ArraySize As Integer Dim NumInList As Integer ‘ Список элементов. ‘ Размер массива. ‘ Число используемых элементов. 1 ‘ Если массив заполнен, увеличить его размер, добавив 10 ячеек. ‘ Затем добавить новый элемент в конец списка. Sub AddToList(value As String) NumInList = NumInList + 1 If NumInList > ArraySize Then ArraySize = ArraySize + 10 ReDim Preserve List(1 To ArraySize) End If List(NumInList) = value End Sub ‘ Удалить последний элемент из списка. Если осталось больше ‘ 20 пустых ячеек, уменьшить список, освобождая память. Sub RemoveFromList() NumInList = NumInList – 1 If ArraySize – NumInList > 20 Then ArraySize = ArraySize –10 ReDim Preserve List(1 To ArraySize) End If End Sub Для очень больших массивов это решение может также оказаться не самым лучшим. Если вам нужен список, содержащий 1000 элементов, к которому обычно добавляется по 100 элементов, то все еще слишком много времени будет тратиться на изменение размера массива. Очевидной стратегией в этом случае было бы увеличение приращения размера массива с 10 до 100 или более ячеек. Тогда можно было бы добавлять по 100 элементов одновременно без частого изменения размера списка. Более гибким решением будет изменение приращения в зависимости от размера массива. Для небольших списков это приращение было бы также небольшим. Хотя изменения размера массива происходили бы чаще, они потребовали бы относительно немного времени для небольших массивов. Для больших списков, приращение размера будет больше, поэтому их размер будет изменяться реже. Следующая программа пытается поддерживать примерно 10 процентов списка свободным. Когда массив заполняется, его размер увеличивается на 10 процентов. Если свободное пространство составляет более 20 процентов от размера массива, программа уменьшает его. При увеличении размера массива, добавляется не меньше 10 элементов, даже если 10 процентов от размера массива составляют меньшую величину. Это уменьшает число необходимых изменений размера массива, если список очень мал. Const WANT_FREE_PERCENT = .1 Const MIN_FREE = 10 Global List() As String Global ArraySize As Integer Global NumItems As Integer Global ShrinkWhen As Integer ‘ ‘ ‘ ‘ ‘ ‘ 10% свободного места. Минимальное число пустых ячеек. Массив элементов списка. Размер массива. Число элементов в списке. Уменьшить размер, если NumItems < ShrinkWhen. ‘ Если массив заполнен, увеличить его размер. ‘ Затем добавить новый элемент в конец списка. Sub Add(value As String) NumItems = NumItems + 1 If NumItems > ArraySize Then ResizeList List(NumItems) = value End Sub ‘ Удалить последний элемент из списка. ‘ Если в массиве много пустых ячеек, уменьшить его размер. Sub RemoveLast() NumItems = NumItems – 1 If NumItems < ShrinkWhen Then ResizeList End Sub ‘ Увеличить размер массива, чтобы 10% ячеек были свободны. Sub ResizeList() Dim want_free As Integer 2 want_free = WANT_FREE_PERCENT * NumItems If want_free < MIN_FREE Then want_free = MIN_FREE ArraySize = NumItems + want_free ReDim Preserve List(1 To ArraySize) ‘ Уменьшить размер массива, если NumItems < ShrinkWhen. ShrinkWhen = NumItems – want_free End Sub Option Explicit Const WANT_FREE_PERCENT = 0.1 Const MIN_FREE = 10 Private m_List() As Variant Private m_NumItems As Long Private m_ArraySize As Long Private m_ShrinkWhen As Long ' ' ' ' ' ' Try for 50% free space. Min unused space when resizing. The list array. Last index in use. Size of the list array. Shrink if m_NumItems < this. Function Item(ByVal position As Long) As Variant If position < 1 Or position > m_NumItems Then Item = Null Else ' Return the item. Item = m_List(position) End If End Function ' Out of bounds. Return Null. Property Get NumItems() As Long NumItems = m_NumItems End Property Property Get ArraySize() As Long ArraySize = m_ArraySize End Property Public Sub RemoveLast() m_NumItems = m_NumItems - 1 If m_NumItems < m_ShrinkWhen Then ResizeList End Sub Public Sub Add(ByVal value As Variant) m_NumItems = m_NumItems + 1 If m_NumItems > m_ArraySize Then ResizeList m_List(m_NumItems) = value End Sub Private Sub ResizeList() Dim want_free As Integer want_free = WANT_FREE_PERCENT * m_NumItems If want_free < MIN_FREE Then want_free = MIN_FREE m_ArraySize = m_NumItems + want_free ReDim Preserve m_List(1 To m_ArraySize) ' We will shrink the array if SL_NumItems < SL_ShrinkWhen. m_ShrinkWhen = m_NumItems - want_free ' Comment when not testing. MsgBox "Resizing." & vbCrLf & vbCrLf & _ "Items:" & Str$(m_ArraySize) & vbCrLf & _ "Unused: " & Str$(want_free) & vbCrLf & _ "ShrinkWhen:" & Str$(m_ShrinkWhen), _ vbOKOnly, "SimpleList" End Sub Dim List1 As New SimpleList Dim List2 As New SimpleList 3