Лекция 9 ( 309 )

advertisement
Лекция 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
Download