VBAでArrayListクラスを実装した

VBAでは.NETのSystem.Collections.ArrayListを使うことができますが、環境によって動かなかったりします。

それを解決するため、クラスでArrayListを実装したのでこの記事で紹介していきます。

クラス概要

Java, Python, Ruby, C#などを参考に、自分が欲しいと思う機能をほぼ実装しました。
ポイントとしては以下の通りです。
・map, filter, flattenの実装
pythonのようにマイナスのインデックスに対応 (-1は末尾の要素を指す)
・ソートアルゴリズムクイックソート
・ワークシートへの読み書きに対応
・ToStringの実装、要素を簡単に出力できる
Grepで要素を選択可能
・シャッフル、ランダムな値取得
・uniqの実装
など

使用方法

記事一番下のソースコードをコピーしてVBEに貼り付けます。
その後オブジェクト名(クラス名)を「ArrayList」に変更します。

使用例

簡単に使用例を紹介します

Set arr as New ArrayList

arr.Add 1
arr.AddValues  2, 3, 4
arr.Add Array(5, 6, 7)

arr.Dump 
' => [1, 2, 3, 4, [5, 6, 7]]

arr.Flatten.Dump
' => [1, 2, 3, 4, 5, 6, 7]

Dumpメソッドで簡単に要素を出力できます。

メソッド一覧 (全51個)

メソッド名 説明
Add 末尾に要素を追加する
AddValues 可変長引数で末尾に要素を一括追加する
AddRange 末尾に指定した配列の要素をすべて追加する
AsList 配列をArrayListに変換する
Clear 要素をすべて削除する
Clone このリストをコピーして返す
Compact Null, Emptyをすべて取り除いたリストを返す
Contains 指定した要素が含まれているならTrueを返す
CopyBy 指定した配列をこのリストにコピーする
Count 素数を返す
CountItem 指定した要素を数える
CreateList 指定した要素数でこのリストを初期化する
Drop 先頭からn要素を捨てて残りを返す
Dump このリストをイミディエイトウィンドウに出力する
Filter 各要素に対して関数を評価した値がTrueの要素だけを取得する
First 先頭の要素を返す
Flatten ネストした配列を一次元の配列に変換する
GetItem 要素を取得する
GetItemAsList リストとして要素を取得する
Grep 正規表現にマッチする要素を返す
Grep_v 正規表現にマッチしない要素を返す
IndexOf 指定した値の左からのインデックスを返す
Insert 指定した場所に要素を挿入する
IsNumericArray 数値だけのリストならTrue
Join 要素を指定した文字列で結合する
Last 末尾の値を返す
LastIndexOf 指定した値の右からのインデックスを返す
Map 各要素に対して関数を評価したリストを返す
Max 最大値を返す
Min 最小値を返す
Pop 末尾から要素を取得する
ReadArrayFromWorkSheet ワークシートから配列を読み込む
Remove 指定した要素を一つ削除する
RemoveAll 指定した要素をすべて削除する
RemoveAt 指定した場所の要素を削除する
Reverse 要素を逆順にする
Reversed 要素を逆順にしたリストを返す
Sample 指定数ランダムに選んで取得する
SetItem 指定した場所に要素をセットする
Shuffle シャッフルする
Shuffled シャッフルしたりストを返す
Slice 部分リストを返す
Sort 昇順にソートする
Sorted 昇順にソートしたリストを返す
Sum 要素を合計した値を返す
Take 先頭からn要素を取得する
ToArray 動的配列に変換する
ToNumericList 数値だけのリストに変換する
ToString 文字列表現に変換する
Uniq 重複した要素を削除する
WriteArrayToWorkSheet ワークシートにこのリストを書き込む

ソースコード

1600行ほどあるのでかなり長いです。
ちなみにダブルクリックで全選択できます。

'
' 動的配列への操作を抽象化するクラス
'

Option Explicit
Option Base 0

Private array_ As Variant

'
' このリストのコンストラクタ
'
Private Sub Class_Initialize()

    array_ = Array()
    
End Sub

'
' このリストのデストラクタ
'
Private Sub Class_Terminate()

    Erase array_
    
End Sub

'
' 指定要素数のリストを初期化する
' 2次元配列まで対応
'
Public Sub CreateList(w As LongPtr, Optional h As LongPtr = 1, Optional init As Variant = Null)
    
    Dim i     As LongPtr
    Dim j     As LongPtr
    Dim ret   As New ArrayList
    Dim temp  As New ArrayList
    
    Me.Clear
    
    If h <= 1 Then
    
        For i = 1 To w
            ret.Add init
        Next
        
    Else
    
        For i = 1 To h
            temp.Clear
            For j = 1 To w
                temp.Add init
            Next
            ret.Add temp
        Next
    
    End If
    
    Me.CopyBy ret
    
End Sub

'
' このリストの末尾に指定した要素を追加する
'
Public Sub Add(value As Variant)
    
    Dim item As Variant
    
    ' 引数にArrayListが指定された場合は配列に変換する
    If TypeName(value) = TypeName(Me) Then
    
        item = value.ToArray
        
    ' 引数にObjectが指定された場合は例外を起こす
    ElseIf IsObject(value) Then
    
        Err.Raise 13
    
    ' 引数に静的配列が指定された場合は動的配列に変換する
    ElseIf IsArray(value) Then
        
        item = StaticArrayToDynamicArray(value)
    
    Else
    
        item = value
        
    End If
    
    ReDim Preserve array_(UBound(array_) + 1)
    array_(UBound(array_)) = item
    
End Sub

'
' このリストの末尾に指定した要素を可変長引数で追加する
'
Public Sub AddValues(ParamArray values() As Variant)
    
    Dim value As Variant
    
    For Each value In values
        
        Me.Add value
        
    Next
    
End Sub

'
' このリストの末尾に指定した配列の要素を追加する
'
Public Sub AddRange(arr As Variant)
    
    Dim seq  As Variant
    Dim item As Variant
    
    If TypeName(arr) = TypeName(Me) Then
    
        seq = arr.ToArray
    
    ElseIf IsArray(arr) Then
        
        seq = StaticArrayToDynamicArray(arr)
    
    ElseIf Not IsArray(arr) Then
    
        seq = Array(arr)
        
    Else
    
        seq = arr
        
    End If
    
    For Each item In seq
        
        Me.Add item
        
    Next
    
End Sub

'
' このリストの指定した位置に指定した要素を挿入する
'
Public Sub Insert(idx As LongPtr, value As Variant)
        
    Dim item    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    
    idx = ConvIdx(idx)
    
    If idx < 0 Or idx > UBound(array_) Then
        
        Err.Raise 9
        
    End If
    
    ReDim Preserve array_(UBound(array_) + 1)
    
    length = UBound(array_)
    
    For i = length To idx Step -1
        
        If i > idx Then
            
            array_(i) = array_(i - 1)
            
        Else
            
            array_(i) = value
            
        End If
        
    Next
    
End Sub

'
' 特定の要素がこのリスト内にあるときに、最初に出現したものを削除する
'
Public Sub Remove(value As Variant)
    
    Dim item    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    
    length = UBound(array_)
    
    For i = 0 To length
        
        If IsSameValue(array_(i), value) Then
            
            Me.RemoveAt i
            Exit For
            
        End If
        
    Next
    
End Sub

'
' このリストから指定した要素をすべて削除する
'
Public Sub RemoveAll(value As Variant)
    
    Dim item    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    
    length = UBound(array_)
    
    For i = length To 0 Step -1
        
        If IsSameValue(array_(i), value) Then
            
            Me.RemoveAt i
            
        End If
        
    Next
    
End Sub

'
' このリストの指定された位置にある要素を削除する
'
Public Sub RemoveAt(idx As LongPtr)
    
    Dim i       As LongPtr
    Dim length  As LongPtr
    
    length = Me.Count
    idx = ConvIdx(idx)
    
    If idx < 0 Or idx >= length Then
        
        Err.Raise 9
        
    End If
    
    For i = idx + 1 To length - 1
    
        array_(i - 1) = array_(i)
    
    Next
    
    If length > 1 Then
        ReDim Preserve array_(UBound(array_) - 1)
    Else
        Me.Clear
    End If
    
End Sub

'
' このリストの要素をすべて削除して空にする
'
Public Sub Clear()
    
    Erase array_
    array_ = Array()
    
End Sub

'
' 指定された要素がこのリスト内で最初に検出された位置のインデックスを返す
' 指定された要素がこのリストに存在しない場合は -1 を返す
'
Public Function IndexOf(value As Variant) As LongPtr
    
    Dim item    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    
    length = UBound(array_)
    
    For i = 0 To length
        
        item = array_(i)
        
        If IsSameValue(item, value) Then
            
            IndexOf = i
            Exit Function
            
        End If
        
    Next
    
    IndexOf = -1
    
End Function

'
' 指定された要素がこのリスト内で最後に検出された位置のインデックスを返す
' 指定された要素がこのリストに存在しない場合は -1 を返す
'
Public Function LastIndexOf(value As Variant) As LongPtr
    
    Dim item    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    
    length = UBound(array_)
    
    For i = length To 0 Step -1
        
        item = array_(i)
        
        If IsSameValue(item, value) Then
            
            LastIndexOf = i
            Exit Function
            
        End If
        
    Next
    
    LastIndexOf = -1
    
End Function

'
' このリストの要素数を返す
'
Public Function Count() As LongPtr
    
    Count = UBound(array_) + 1
    
End Function

'
' このリストに指定した要素がいくつ含まれているかを返す
'
Public Function CountItem(value As Variant) As LongPtr
    
    Dim total  As LongPtr
    Dim item   As Variant
    
    For Each item In array_
    
        If IsSameValue(item, value) Then
            total = total + 1
        End If
        
    Next
    
    CountItem = total
    
End Function

'
' このリストに指定した要素が含まれているならTrueを返す
'
Public Function Contains(value As Variant) As Boolean
    
    Dim item As Variant
    
    For Each item In array_
    
        If IsSameValue(item, value) Then
            
            Contains = True
            Exit Function
            
        End If
    
    Next
    
    Contains = False
    
End Function

'
' このリストの指定位置の要素を返す
'
Public Function GetItem(idx As LongPtr) As Variant
    
    idx = ConvIdx(idx)
    GetItem = array_(idx)
    
End Function

'
' このリストの指定位置の要素をArrayListに変換して返す
'
Public Function GetItemAsList(idx As LongPtr) As ArrayList
    
    Dim ret As New ArrayList
    
    idx = ConvIdx(idx)
    
    ret.AddRange array_(idx)
    
    Set GetItemAsList = ret

End Function

'
' このリストの指定位置に要素をセットする
'
Public Sub SetItem(idx As LongPtr, value As Variant)
    
    idx = ConvIdx(idx)
    array_(idx) = value
    
End Sub

'
' このリストの指定された位置にある要素をリストから削除して、その要素を返す
' インデックスが指定されない場合は、リストの末尾の要素を削除して返す
'
Public Function Pop(Optional idx As LongPtr = -1) As Variant
    
    idx = ConvIdx(idx)
    
    Pop = array_(idx)
    
    Me.RemoveAt idx
    
End Function

'
' このリストの要素を指定数ランダムに選んでArrayListとして返す
'
Public Function Sample(Optional n As LongPtr = 1) As ArrayList
    
    Dim i     As LongPtr
    Dim temp  As ArrayList
    Dim ret   As New ArrayList
    
    Set temp = Me.Clone
    temp.Shuffle
    
    For i = 1 To n
        
        ret.Add temp.Pop
        
    Next
    
    Set Sample = ret
    
End Function

'
' このリストの要素を逆順にする
'
Public Sub Reverse()

    Dim rIdx  As LongPtr
    Dim lIdx  As LongPtr
    
    rIdx = 0
    lIdx = Me.Count - 1
    
    While rIdx < lIdx
        
        Swap rIdx, lIdx
        
        rIdx = rIdx + 1
        lIdx = lIdx - 1
        
    Wend
    
End Sub

'
' このリストの要素を逆順にしたリストを返す
'
Public Function Reversed() As ArrayList
    
    Dim ret As ArrayList
    
    Set ret = Me.Clone
    ret.Reverse
    
    Set Reversed = ret
    
End Function

'
' このリストの要素をシャッフルする
'
Public Sub Shuffle()
    
    Dim i       As LongPtr
    Dim r       As LongPtr
    Dim length  As LongPtr
    
    length = Me.Count
    
    For i = length - 1 To 2 Step -1
        
        r = Rnd * 10000 + length
        r = r Mod (i - 1)
        
        Swap r, i
        
    Next
    
End Sub

'
' このリストの要素をシャッフルしたリストを返す
'
Public Function Shuffled() As ArrayList

    Dim ret As ArrayList
    
    Set ret = Me.Clone
    ret.Shuffle
    
    Set Shuffled = ret
    
End Function

'
' このリストの要素を昇順で並び替える
'
Public Sub Sort()
    
    If Me.IsNumericArray() Then
        
        Call SortByNumeric
        
    Else
    
        Call SortByString
    
    End If
    
End Sub

'
' このリストの要素を昇順で並び替えたリストを返す
'
Public Function Sorted() As ArrayList
    
    Dim ret As ArrayList
    
    Set ret = Me.Clone
    
    ret.Sort
    
    Set Sorted = ret
    
End Function

'
' このリストを数値の配列とみなして最大値を返す
'
Public Function Max() As Double
    
    Dim item    As Variant
    Dim prev    As Double
    Dim i       As LongPtr

    ' リストに数値以外が含まれている場合はエラー
    If Me.IsNumericArray = False Then
        Err.Raise 13
    End If
    
    ' リストに要素が無い場合はエラー
    If Me.Count = 0 Then
        Err.Raise 9
    End If
    
    prev = -1E+18
    
    For Each item In array_
        
        If item > prev Then
            
            prev = item
            
        End If
        
    Next
    
    Max = prev
    
End Function

'
' このリストを数値の配列とみなして最小値を返す
'
Public Function Min() As Double
    
    Dim item    As Variant
    Dim prev    As Double
    Dim i       As LongPtr

    ' リストに数値以外が含まれている場合はエラー
    If Me.IsNumericArray = False Then
        Err.Raise 13
    End If
    
    ' リストに要素が無い場合はエラー
    If Me.Count = 0 Then
        Err.Raise 9
    End If
    
    prev = 1E+32
    
    For Each item In array_
        
        If CDbl(item) < prev Then
            
            prev = item
            
        End If
        
    Next
    
    Min = prev
    
End Function

'
' このリストを数値の配列とみなして合計値を返す
'
Public Function Sum() As Double
    
    Dim item   As Variant
    Dim total  As Double
    
    ' リストに数値以外が含まれている場合はエラー
    If Me.IsNumericArray = False Then
        Err.Raise 13
    End If
    
    ' リストに要素が無い場合はエラー
    If Me.Count = 0 Then
        Err.Raise 9
    End If
    
    For Each item In array_
        total = total + CDbl(item)
    Next
    
    Sum = total
    
End Function

'
' このリストの先頭の要素を取得する
' 要素数が0の時はEmptyを返す
'
Public Function First() As Variant
    
    If Me.Count = 0 Then
        
        First = Empty
        
    Else
        
        First = array_(0)
    
    End If
    
End Function

'
' このリストの末尾の要素を取得する
' 要素数が0の時はEmptyを返す
'
Public Function Last() As Variant
    
    If Me.Count = 0 Then
        
        Last = Empty
        
    Else
        
        Last = array_(ConvIdx(-1))
    
    End If
    
End Function

'
' このリストの重複した要素を取り除いたリストを返す
'
Public Function Uniq() As ArrayList
    
    Dim item    As Variant
    Dim prev    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    Dim temp    As ArrayList
    Dim ret     As New ArrayList
    
    Set temp = Me.Sorted
    
    length = temp.Count
    
    If length = 0 Then
        Exit Function
    End If
    
    prev = temp.GetItem(0)
    
    ret.Add prev
    
    For i = 1 To length - 1
        
        item = temp.GetItem(i)
        
        If Not IsSameValue(prev, item) Then
            
            ret.Add item
            
        End If
        
        prev = item
    
    Next
    
    Set Uniq = ret
    
End Function

'
' このリストを再帰的に平坦化したリストを生成して返す
'
Public Function Flatten() As ArrayList
    
    Dim ret   As New ArrayList
    Dim tmp   As New ArrayList
    Dim item  As Variant
    
    For Each item In array_
        
        If IsArray(item) Then
            
            tmp.CopyBy item
            ret.AddRange tmp.Flatten
            
        Else
            
            ret.Add item
            
        End If
        
    Next
    
    Set Flatten = ret
    
End Function

'
' このリストからNull, Emptyを取り除いたリストを返す
'
Public Function Compact() As ArrayList
    
    Dim item  As Variant
    Dim ret   As New ArrayList
    
    For Each item In array_
        
        If IsNull(item) Or IsEmpty(item) Then
        
        Else
            
            ret.Add item
            
        End If
    
    Next
    
    Set Compact = ret
    
End Function

'
' 各要素に対して関数を評価した結果を全て含むリストを返す
'
Public Function Map(obj As Object, funcname As String) As ArrayList
    
    Dim ret   As New ArrayList
    Dim item  As Variant
    
    For Each item In array_
    
        ret.Add CallByName(obj, funcname, VbMethod, item)
        
    Next

    Set Map = ret
    
End Function

'
' 各要素に対して関数を評価した値がTrueであった要素を全て含む配列を返す
' Trueになる要素がなかった場合は空の配列を返す
'
Public Function Filter(obj As Object, funcname As String) As ArrayList
    
    Dim ret   As New ArrayList
    Dim item  As Variant
    
    For Each item In array_
        
        If CallByName(obj, funcname, VbMethod, item) Then
        
            ret.Add item
        
        End If
        
    Next

    Set Filter = ret
    
End Function

'
' このリストの要素を文字列とみなし、正規表現に一致する要素を選択したリストを返す
'
Public Function Grep(pattern As String) As ArrayList
        
    On Error Resume Next
        
    Dim re      As Object
    Dim ret     As New ArrayList
    Dim item    As Variant
    
    Set re = CreateObject("VBScript.RegExp")
    re.pattern = pattern
    
    For Each item In array_
        
        item = CStr(item)
        
        If re.Test(item) = True Then
            ret.Add item
        End If
    
    Next
    
    Set re = Nothing
    Set Grep = ret
    
End Function

'
' このリストの要素を文字列とみなし、正規表現に一致しない要素を選択したリストを返す
'
Public Function Grep_v(pattern As String) As ArrayList
        
    On Error Resume Next
        
    Dim re      As Object
    Dim ret     As New ArrayList
    Dim item    As Variant
    
    Set re = CreateObject("VBScript.RegExp")
    re.pattern = pattern
    
    For Each item In array_
        
        item = CStr(item)
        
        If re.Test(item) = False Then
            ret.Add item
        End If
    
    Next
    
    Set re = Nothing
    Set Grep_v = ret
    
End Function

'
' このリストの要素を文字列sepを間に挟んで連結した文字列を返す
'
Public Function Join(Optional sep = " ") As String
    
    Dim temp  As String
    Dim item  As Variant
    
    For Each item In array_
        
        temp = temp & item & sep
    
    Next
    
    temp = Left(temp, Len(temp) - Len(sep))
    Join = temp

End Function

'
' idxStartからindexEndまでの要素を取り出したリストを返す
'
Public Function Slice(idxStart As LongPtr, idxEnd As LongPtr, Optional step As LongPtr = 1) As ArrayList
    
    Dim i    As LongPtr
    Dim ret  As New ArrayList
    
    idxStart = ConvIdx(idxStart)
    idxEnd = ConvIdx(idxEnd)
    
    If step = 0 Then
        
        Set Slice = ret
        Exit Function
        
    End If
    
    For i = idxStart To idxEnd Step step
        
        ret.Add array_(i)
        
    Next
    
    Set Slice = ret
    
End Function

'
' このリストの先頭から n 要素をArrayListとして返す
'
Public Function Take(n As LongPtr) As ArrayList
    
    Dim item    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    Dim ret     As New ArrayList
    
    length = Me.Count
    
    For i = 0 To length - 1
        
        item = array_(i)
        
        If i = n Then
            Exit For
        End If
        
        ret.Add item
        
    Next
    
    Set Take = ret
   
End Function

'
' 配列の先頭の n 要素を捨て、残りの要素をArrayListとして返す
'
Public Function Drop(n As LongPtr) As ArrayList
    
    Dim item    As Variant
    Dim i       As LongPtr
    Dim length  As LongPtr
    Dim ret     As New ArrayList
    
    length = Me.Count
    
    For i = n To length - 1
        
        item = array_(i)
        
        ret.Add item
        
    Next
    
    Set Drop = ret
    
End Function

'
' 指定した要素をリストに変換して返す
'
Public Function AsList(value As Variant) As ArrayList
    
    Dim ret As New ArrayList
    
    ret.AddRange value
    
    Set AsList = ret
    
End Function

'
' このリストを複製する
'
Public Function Clone() As ArrayList
    
    Dim ret As New ArrayList
    
    ret.CopyBy array_
    
    Set Clone = ret
    
End Function

'
' このリストに要素をコピーする
'
Public Sub CopyBy(value As Variant)
    
    Me.Clear
    Me.AddRange value
    
End Sub

'
' 指定したワークシートから配列を読み込む
' ワークシートを指定しない場合はActiveSheetを指定したとする
'
Public Sub ReadArrayFromWorkSheet(pos As String, Optional sheet As Worksheet = Nothing)
    
    If sheet Is Nothing Then
        Set sheet = ActiveSheet
    End If
    
    Me.Clear
    
    Me.AddRange StaticArrayToDynamicArray(sheet.Range(pos).value, True)
    
End Sub

'
' ワークシートの指定した範囲にこのリストの要素を書き出す
' ワークシートを指定しない場合はActiveSheetを指定したとする
' 二次元配列に書き出す場合、左上から左->右とセルに代入していく
'
Public Sub WriteArrayToWorkSheet(pos As String, Optional sheet As Worksheet = Nothing)
    
    Dim i     As LongPtr
    Dim j     As LongPtr
    Dim temp  As Variant
    Dim elms  As New ArrayList
    
    Set elms = Me.Flatten
    
    If sheet Is Nothing Then
        Set sheet = ActiveSheet
    End If
    
    temp = sheet.Range(pos).value
    
    For i = LBound(temp, 1) To UBound(temp, 1)
    
        For j = LBound(temp, 2) To UBound(temp, 2)
            
            If elms.Count > 0 Then
                temp(i, j) = elms.Pop(0)
            Else
                temp(i, j) = Empty
            End If
            
        Next
        
    Next
    
    sheet.Range(pos) = temp
    
End Sub


'
' このリストの要素が数値だけの場合Trueを返す
'
Public Function IsNumericArray() As Boolean
    
    Dim item As Variant
    
    For Each item In array_
    
        If Not IsNumeric(item) Then
            IsNumericArray = False
            Exit Function
        End If
        
    Next
    
    IsNumericArray = True
    
End Function

'
' このリストの要素を文字列に変換して返す
'
Public Function ToString() As String
    
    Dim ret     As String
    Dim i       As LongPtr
    Dim item    As Variant
    Dim length  As LongPtr
    
    length = Me.Count
    
    If length = 0 Then
        ToString = "[]"
        Exit Function
    End If
    
    ret = "["
    
    For i = 0 To length - 2
        
        item = array_(i)
        
        ret = ret & ConvertItemToString(item) & ", "
        
    Next
    
    item = array_(length - 1)
    ret = ret & ConvertItemToString(item) & "]"
    
    ToString = ret
    
End Function

'
' このリストの数値の要素のみを選択したリストを返す
'
Public Function ToNumericList() As ArrayList
    
    Dim ret   As New ArrayList
    Dim item  As Variant
    
    For Each item In array_
    
        If IsNumeric(item) Then
            
            If item <> Empty Then
            
                ret.Add item
            
            End If
            
        End If
        
    Next
    
    Set ToNumericList = ret
    
End Function

'
' このリストを動的配列に変換して返す
'
Public Function ToArray() As Variant
    
    ToArray = array_
    
End Function

'
' このリストの全ての要素をイミディエイトウィンドウに出力する
'
Public Sub Dump()
    
    Debug.Print Me.ToString
    
End Sub

'
' * Private Methods *
'

'
' このリストの要素の位置を交換する
'
Private Sub Swap(idx1 As LongPtr, idx2 As LongPtr)
    
    Dim temp As Variant
    temp = array_(idx1)
    array_(idx1) = array_(idx2)
    array_(idx2) = temp
    
End Sub

'
' 指定した要素の文字列表現を返す
'
Private Function ConvertItemToString(value As Variant)
    
    Dim ret As String
    
    ' 引数が文字列の場合
    If TypeName(value) = "String" Then
        
        value = Replace(value, vbCrLf, "\r\n")
        value = Replace(value, vbLf, "\n")
        value = Replace(value, vbCr, "\r")
        value = Replace(value, """", "\""")
        ret = """" & value & """"
    
    ' 引数が配列の場合
    ElseIf IsArray(value) Then
        
        Dim temp As New ArrayList
        temp.CopyBy value
        ret = temp.ToString
    
    ' 引数がEmptyの場合
    ElseIf IsEmpty(value) Then
        
        ret = "Empty"
    
    ' 引数がNullの場合
    ElseIf IsNull(value) Then
    
        ret = "Null"
    
    ' 引数がオブジェクトの場合
    ElseIf IsObject(value) Then
        
        ret = "<" & TypeName(value) & " object at " & ObjPtr(value) & ">"
    
    ' 引数がBooleanの場合
    ElseIf TypeName(value) = "Boolean" Then
    
        ret = StrConv(value, vbProperCase)
    
    ' 引数がエラーの場合
    ElseIf IsError(value) Then
        
        ret = "<error """ & TypeName(value) & """>"
    
    ' 引数がDateの場合
    ElseIf IsDate(value) Then
        
        ret = value
    
    ' 引数が数値の場合
    ElseIf IsNumeric(value) Then
        
        ret = value
    
    ' その他の場合
    Else
        
        ret = """" & value & """"
        
    End If
    
    ConvertItemToString = ret
    
End Function

'
' 配列同士の比較をする
'
Private Function IsSameArray(a As Variant, b As Variant) As Boolean
    
    Dim i       As LongPtr
    Dim length  As LongPtr
    Dim arrA    As New ArrayList
    Dim arrB    As New ArrayList
    Dim itemA   As Variant
    Dim itemB   As Variant

    arrA.AddRange a
    arrB.AddRange b

    If arrA.Count <> arrB.Count Then
        IsSameArray = False
        Exit Function
    End If

    If arrA.Count = 0 And arrB.Count = 0 Then
        IsSameArray = True
        Exit Function
    End If
    
    length = arrA.Count
    
    For i = 0 To length - 1
        
        itemA = arrA.GetItem(i)
        itemB = arrB.GetItem(i)
        
        If IsArray(itemA) Or IsArray(itemB) Then
            
            If IsArray(itemA) And IsArray(itemB) Then

                If Not IsSameArray(itemA, itemB) Then
                    IsSameArray = False
                    Exit Function
                End If
            
            Else
                IsSameArray = False
                Exit Function
            End If
        
        Else
            
            If itemA <> itemB Then
                
                IsSameArray = False
                Exit Function
                
            End If
        
        End If
    
    Next
    
    IsSameArray = True
    
End Function

'
' 配列, オブジェクト, 値の差を気にせず比較できるようにする
'
Private Function IsSameValue(a As Variant, b As Variant) As Boolean
    
    Dim ret As Boolean
    
    ' 配列同士の比較
    If IsArray(a) Or IsArray(b) Then
                
        If IsArray(a) And IsArray(b) Then
            
            ret = IsSameArray(a, b)
            
        Else
            
            ret = False
            
        End If
    
    ' オブジェクト同士の比較
    ElseIf IsObject(a) Or IsObject(b) Then
        
        If IsObject(a) And IsObject(b) Then

            ret = ObjPtr(a) = ObjPtr(b)
            
        Else
        
        ret = False
        
        End If
    
    ' Null同士の比較
    ElseIf IsNull(a) Or IsNull(b) Then
    
        If IsNull(a) And IsNull(b) Then
        
            ret = True
        
        Else
            
            ret = False
        
        End If
    
    ' 値同士の比較
    Else
        
        ret = a = b
    
    End If
    
    IsSameValue = ret
    
End Function

'
' 指定した配列の次元数を取得する
'
Private Function GetArrayDimension(arr As Variant) As Integer
    
    Dim size  As LongPtr
    Dim i     As LongPtr
    
    On Error GoTo L_RET
    
    While True
    
        i = i + 1
        size = UBound(arr, i) - 1

    Wend
    
L_RET:

    GetArrayDimension = i - 1

End Function

'
' 指定した静的配列を動的配列に変換する
' 2次元まで対応
'
Private Function StaticArrayToDynamicArray(arr As Variant, Optional isWorksheet = False) As Variant
    
    Dim i     As LongPtr
    Dim j     As LongPtr
    Dim w     As LongPtr
    Dim h     As LongPtr
    Dim temp  As Variant
    Dim item  As Variant
    Dim ret   As Variant
    
    ret = Array()
    
    If GetArrayDimension(arr) = 2 Then
    
        h = UBound(arr, 1)
        w = UBound(arr, 2)
        
        For i = LBound(arr, 1) To h
            
            temp = Array()
            
            For j = LBound(arr, 2) To w
                
                ReDim Preserve temp(UBound(temp) + 1)
                temp(UBound(temp)) = arr(i, j)
                
            Next
            
            ReDim Preserve ret(UBound(ret) + 1)
            ret(UBound(ret)) = temp
            
        Next
        
        ' 指定した静的配列がワークシートから取得した配列の場合は変換をかける
        If isWorksheet = True Then
        
            If UBound(ret) = 0 Then
                ret = ret(0)
            
            ElseIf UBound(ret(LBound(ret))) = 0 Then
                temp = Array()
                For Each item In ret
            
                    ReDim Preserve temp(UBound(temp) + 1)
                    temp(UBound(temp)) = item(0)
            
                Next
                ret = temp
            End If
            
        End If
        
        StaticArrayToDynamicArray = ret
    
    Else
        
        For Each item In arr
            
            ReDim Preserve ret(UBound(ret) + 1)
            ret(UBound(ret)) = item
            
        Next
        
        StaticArrayToDynamicArray = ret
    
    End If
    
End Function

'
' マイナスのインデックスを変換する
'
Private Function ConvIdx(n As LongPtr) As LongPtr
    
    
    If n >= 0 Then
    
        ConvIdx = n
        
    Else
        
        ConvIdx = Me.Count + n
        
    End If

End Function

'
' このリストの要素を数値として照準で並び替える
'
Private Sub SortByNumeric()

    Call quicksort_n(array_)

End Sub

'
' このリストの要素を文字列として照準で並び替える
'
Private Sub SortByString()
    
    Call quicksort_s(array_)
    
End Sub

'
' 指定した数値の配列をクイックソートで並び変える
'
Private Sub quicksort_n(ByRef data As Variant, Optional low As LongPtr = 0, Optional high As LongPtr = -100)

    Dim l      As LongPtr
    Dim r      As LongPtr
    Dim pivot  As Double

    If high = -100 Then
        
        high = UBound(data)
        
    End If
    
    l = low
    r = high
    
    pivot = data((low + high) \ 2)
    
    Do While l <= r
    
        Do While CDbl(data(l)) < pivot And l < high
            l = l + 1
        Loop
        
        Do While pivot < CDbl(data(r)) And r > low
            r = r - 1
        Loop
    
        If l <= r Then
            Call Swap(l, r)
            l = l + 1
            r = r - 1
        End If
    Loop
    
    If low < r Then
        Call quicksort_n(data, low, r)
    End If
    
    If l < high Then
        Call quicksort_n(data, l, high)
    End If

End Sub

'
' 指定した文字列の配列をクイックソートで並び変える
'
Private Sub quicksort_s(ByRef data As Variant, Optional low As LongPtr = 0, Optional high As LongPtr = -100)

    Dim l      As LongPtr
    Dim r      As LongPtr
    Dim pivot  As Variant

    If high = -100 Then
        
        high = UBound(data)
        
    End If
    
    l = low
    r = high
    
    pivot = ConvertItemToString(data((low + high) \ 2))
    
    Do While l <= r
    
        Do While StrComp(ConvertItemToString(data(l)), pivot) = -1 And l < high
            l = l + 1
        Loop
        
        Do While StrComp(ConvertItemToString(data(r)), pivot) = 1 And r > low
            r = r - 1
        Loop
    
        If l <= r Then
            Call Swap(l, r)
            l = l + 1
            r = r - 1
        End If
    Loop
    
    If low < r Then
        Call quicksort_s(data, low, r)
    End If
    
    If l < high Then
        Call quicksort_s(data, l, high)
    End If

End Sub