您的位置:首页 > 编程语言 > VB

VBA ArrayList类 还可以继续扩展

2014-04-03 20:59 302 查看
Option Explicit

Private m_elements() As Variant
Private m_size As Long
Private m_capacity As Long
Private m_dic As Dictionary

Private Sub Class_Initialize()
ReDim m_elements(9)
m_size = 0
m_capacity = 10
Set m_dic = New Dictionary
End Sub

Public Property Get Capacity() As Long
'all capacity in the array, including unused space
Capacity = UBound(m_elements) + 1
End Property

Public Property Let Capacity(ByVal TotalCapacity As Long)
ReDim Preserve m_elements(TotalCapacity - 1)
m_capacity = TotalCapacity
End Property

Public Function Length() As Long
'includes only used elements in the array
Length = m_size
End Function

Private Sub trimToSize()
'If capacity is large and length < 50% of capacity,
'trim total capacity to: (number of used elements * 1.5)

If m_capacity > 99 Then
If (m_size < (m_capacity / 2)) Then
Dim newUBound As Long
newUBound = Conversion.CLng(m_size * 1.5)
If newUBound < 9 Then 'need at least 10 els
newUBound = 9
End If
ReDim Preserve m_elements(newUBound)
m_capacity = newUBound + 1
End If
End If
End Sub

Private Sub ensureCapacity(ByVal minCapacity As Long)
If m_capacity < minCapacity Then
Dim newUBound As Long
newUBound = Conversion.CLng(m_capacity * 1.5)
ReDim Preserve m_elements(newUBound)
m_capacity = newUBound + 1
End If
End Sub

Public Function isEmpty() As Boolean
isEmpty = (m_size = 0)
End Function

Public Sub Add(Item As Variant, Optional Key As String = "", Optional ByVal Before As Long = -1)
'Inserts the specified element at the specified position in this
'list. Shifts the element currently at that position (if any) and
'any subsequent elements to the right (adds one to their indices).
Call ensureCapacity(m_size + 1)

'shift everything to the right of Before by 1
If (Before > -1) Then
checkIndex (Before)
Dim temp() As Variant
ReDim temp(m_size)

Call arrayCopy(m_elements(), Before, temp(), 0, m_size - Before)
Call arrayCopy(temp(), 0, m_elements(), Before + 1, m_size - Before)
If Not IsObject(Item) Then
m_elements(Before) = Item
Else
Set m_elements(Before) = Item
End If

If Key <> "" Then
If m_dic.Exists(Key) Then Call Err.Raise(Key, Description:="The Key can not allowed repeat")
m_dic.Add Key, m_elements(Before)
End If

Else ' no "Before" param
If Not IsObject(Item) Then
m_elements(m_size) = Item
Else
Set m_elements(m_size) = Item
End If

If Key <> "" Then
If m_dic.Exists(Key) Then Call Err.Raise(Key, Description:="The Key can not allowed repeat")
m_dic.Add Key, Item
End If

End If
m_size = m_size + 1
End Sub

Sub removeAt(ByVal index As Long)
checkIndex (index)

If index < m_size - 1 Then
Dim i As Integer
For i = index To m_size - 1
If Not IsObject(m_elements(i + 1)) Then
m_elements(i) = m_elements(i + 1)
Else
Set m_elements(i) = m_elements(i + 1)
End If
Next i
m_elements(m_size - 1) = Empty

ElseIf index = m_size - 1 Then
m_elements(m_size - 1) = Empty
End If

m_size = m_size - 1
Call trimToSize
End Sub

Public Property Get ItemByKey(ByVal Key As String) As Variant
If m_dic.Exists(Key) Then
If IsObject(m_dic(Key)) Then
Set ItemByKey = m_dic(Key)
Exit Property
Else
ItemByKey = m_dic(Key)
End If
Else
Call Err.Raise(Key, Description:="The Key can not find")
End If
End Property

Public Property Get ItemByIndex(ByVal index As Long) As Variant
If IsObject(m_elements(index - 1)) Then
Set ItemByIndex = m_elements(index - 1)
Exit Property
Else
ItemByIndex = m_elements(index - 1)
End If
End Property

Public Property Let ItemByIndex(ByVal index As Long, ByVal value As Variant)
checkIndex (index - 1)
If IsObject(value) Then
Set m_elements(index - 1) = value
Else
m_elements(index - 1) = value
End If
End Property

Public Sub Remove(ByVal objElement As Variant)
'Remove the first occurrence of the given objElement
Dim i As Long
For i = 0 To m_size - 1
If (m_elements(i) = objElement) Then
Call Me.removeAt(i)
Exit For
End If
Next i
End Sub

Public Sub RemoveAll(ByVal objElement As Variant)
'Remove all occurrences of objElement
Dim changes As Long
changes = 0
Dim i As Long
For i = 0 To m_size - 1
If (m_elements(i - changes) = objElement) Then
Call Me.removeAt(i - changes) ' will decrement m_size
changes = changes + 1
End If
Next i
Call trimToSize
End Sub

Public Sub RemoveRange(ByVal StartingIndex As Long, ByVal EndingIndex As Long)
'startindex= first element to remove index, endingindex=final element to remove
'TODO: what if startindex > endindex?
checkIndex (StartingIndex)
checkIndex (EndingIndex)

Dim oldm_size As Long
oldm_size = m_size
'get all the elements to the right of the range (if there are any elements to the right)
If EndingIndex < m_size - 1 Then
Dim temp() As Variant
temp = Me.Items(EndingIndex + 1, m_size - 1)
Call arrayCopy(temp, 0, m_elements, StartingIndex, UBound(temp) + 1)
End If

m_size = m_size - (EndingIndex - StartingIndex + 1)
Dim i As Long
For i = m_size To oldm_size - 1
m_elements(i) = Empty
Next i
End Sub

Public Function Contains(ByRef Element As Variant) As Boolean
Dim result As Boolean
result = False
Dim i As Long
Dim e As Variant
For Each e In m_elements
If IsObject(Element) Then
If e Is Element Then
result = True
Exit For
End If
Else
If e = Element Then
result = True
Exit For
End If
End If
i = i + 1
If i = m_size Then Exit For
Next e
Contains = result
End Function

Public Function indexOf(ByVal Element As Variant) As Long
'Searches for the specified Object and returns the zero-based index of
'the first occurrence within the entire ArrayList.
'Returns -1 if the Element was not found

Dim result As Long
result = -1
Dim index As Long
index = 0

Dim e As Variant
For Each e In m_elements
If e = Element Then
result = index
Exit For
End If
index = index + 1
Next e

indexOf = result
End Function

Public Function LastIndexOf(ByVal Element As Variant) As Long
'Searches for the specified Object and returns the
'zero-based index of the last occurrence within the entire ArrayList.
'Returns -1 if not found

Dim result As Long
result = -1

Dim i As Long
For i = m_size - 1 To 0 Step -1
If m_elements(i) = Element Then
result = i
Exit For
End If
Next i
LastIndexOf = result
End Function

Public Sub Clear()
ReDim m_elements(9)
m_capacity = 10
m_size = 0
End Sub

Private Sub checkIndex(ByVal index As Long)
If (index >= m_size) Or (index < 0) Then
Call Err.Raise(index, Description:="The index specified is out of bounds")
End If
End Sub

Public Sub Swap(ByVal Index1 As Long, ByVal Index2 As Long)
Dim temp As Variant
checkIndex (Index1)
checkIndex (Index2)

If Not IsObject(m_elements(Index2)) Then
temp = m_elements(Index2)
Else: Set temp = m_elements(Index2)
End If

If Not IsObject(m_elements(Index1)) Then
m_elements(Index2) = m_elements(Index1)
Else
Set m_elements(Index2) = m_elements(Index1)
End If

If Not IsObject(temp) Then
m_elements(Index1) = temp
Else
Set m_elements(Index1) = temp
End If
End Sub

Public Sub Reverse()
If m_size > 1 Then
Dim hiIndex As Long
hiIndex = m_size - 1
Dim loIndex As Long
loIndex = 0
Do While (hiIndex > loIndex)
Call Swap(loIndex, hiIndex)
hiIndex = hiIndex - 1
loIndex = loIndex + 1
Loop
End If
End Sub

Public Sub Shuffle()
'uses Fisher-Yates algo
Dim i As Long
Dim randomNbr As Long
For i = m_size - 1 To 1 Step -1
Randomize
'random integer with 0 <= rndnbr <= i, uniformly distributed
randomNbr = Int((i + 1) * Rnd)
Call Swap(randomNbr, i)
Next i
End Sub

Public Function GetDistinctValues() As ArrayList
Dim distinctVals As New ArrayList
Dim e As Variant
For Each e In m_elements
If Not distinctVals.Contains(e) Then
distinctVals.Add e
End If
Next e
Set GetDistinctValues = distinctVals
End Function

Public Function GetRange(ByVal StartingIndex As Long, ByVal TotalElementsToGet As Long) _
As ArrayList
'Returns a subset of the elements in this ArrayList.
'Index: The 0-based array index at which the range starts.
'Count: The number of elements in the range to get.

Dim newAL As ArrayList
Set newAL = New ArrayList
If TotalElementsToGet > 0 Then
Dim i As Long
If TotalElementsToGet > 9 Then
newAL.Capacity = TotalElementsToGet
Else: newAL.Capacity = 10
End If
For i = StartingIndex To (StartingIndex + TotalElementsToGet - 1)
newAL.Add m_elements(i)
Next i
End If
Set GetRange = newAL
End Function

Public Sub arrayCopy(array1() As Variant, ByVal startingIndex1 As Long, array2() As Variant, _
startingIndex2 As Long, ByVal TotalElements As Long)
On Error Resume Next
'copies from arr1, starting at stin1, to arr2, starting at stin2, TotalElements.
'both arrays must be declared using syntax: dim array1(<number>) as <datatype> or redim array1(<number>)

'ensure arr2 has at least TE els
If UBound(array2) < TotalElements - 1 Then
ReDim Preserve array2(TotalElements - 1)
End If

Dim i As Long
Dim j As Long
j = startingIndex2
For i = startingIndex1 To startingIndex1 + TotalElements - 1
If Not IsObject(array1(i)) Then
array2(j) = array1(i)
Else: Set array2(j) = array1(i)
End If
j = j + 1
Next i
End Sub

Public Sub Sort()
'use quicksort algo
If Me.ContainsObjects() Then
MsgBox "This VBArrayList contains at least 1 object. Quicksort only works on alphanumeric values."
Exit Sub
Else
Call QuickSort
End If
End Sub

Private Sub QuickSort(Optional intLeft As Long = -2, _
Optional intRight As Long = -2)

Dim i As Long
Dim j As Long
Dim varTestVal As Variant
Dim intMid As Long
If intLeft = -2 Then intLeft = 0
If intRight = -2 Then intRight = m_size - 1
If intLeft < intRight Then
intMid = (intLeft + intRight) \ 2
varTestVal = m_elements(intMid)
i = intLeft
j = intRight
Do
Do While m_elements(i) < varTestVal
i = i + 1
Loop
Do While m_elements(j) > varTestVal
j = j - 1
Loop
If i <= j Then
Call Me.Swap(i, j)
i = i + 1
j = j - 1
End If
Loop Until i > j

If j <= intMid Then
Call QuickSort(intLeft, j)
Call QuickSort(i, intRight)
Else
Call QuickSort(i, intRight)
Call QuickSort(intLeft, j)
End If
End If
End Sub

Public Function ContainsObjects() As Boolean
Dim result As Boolean
result = False
Dim e As Variant
For Each e In m_elements
If IsObject(e) Then
result = True
Exit For
End If
Next e
ContainsObjects = result
End Function

Public Function Items(Optional ByVal StartingIndex As Long = 0, Optional ByVal EndingIndex As Long = -1) As Variant()
If EndingIndex = -1 Then EndingIndex = m_size - 1

Dim els() As Variant
ReDim els(EndingIndex - StartingIndex)

Dim i As Long
Dim j As Long
j = 0

If StartingIndex <= EndingIndex Then
For i = StartingIndex To EndingIndex
If Not IsObject(m_elements(i)) Then
els(j) = m_elements(i)
Else
Set els(j) = m_elements(i)
End If
j = j + 1
Next i
Else
For i = StartingIndex To EndingIndex Step -1
If Not IsObject(m_elements(i)) Then
els(j) = m_elements(i)
Else
Set els(j) = m_elements(i)
End If
j = j + 1
Next i
End If

Items = els
End Function

Public Function ToCollection() As Collection
Dim coll As New Collection
Dim i As Long
For i = 0 To m_size - 1
coll.Add m_elements(i)
Next i
Set ToCollection = coll
End Function

Public Function ToArray() As Variant()
ToArray = m_elements
End Function

Public Sub IntakeArray(yourArray() As Variant)
'array must be a variant array
m_elements = yourArray
m_capacity = Me.Capacity
m_size = Me.Length
End Sub

Public Sub IntakeCollection(ByVal yourCollection As Collection)
'completely replaces anything in m_elements with the elements of a collection
'do not use parentheses around the argument

ReDim m_elements(yourCollection.Count - 1)
Dim i As Long
For i = 0 To UBound(m_elements)
If IsObject(yourCollection.Item(i + 1)) Then
Set m_elements(i) = yourCollection.Item(i + 1)
Else: m_elements(i) = yourCollection.Item(i + 1)
End If
Next i
m_capacity = Me.Capacity
m_size = Me.Length
End Sub
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐