2.9. java.util.ArrayList。配列をすごく便利にします。

ArrayListは配列を使いやすく拡張したクラスです。

配列はそのままでは扱いにくいので、ArrayListを使いましょう。

削除や挿入など、配列を操作する機能が盛りだくさんで、とても便利です。

※ArrayListは結構機能が多くて、ドキュメントが大きくなりました。

目次

2.9.1. クラス図へ変換

では、早速クラス図にしてみます。

UML変換くんを実行。

> touml .\java\util\ArrayList.java

> touml .\java\util\AbstarctList.java

> touml .\java\util\AbstractCollection.java

※java1.8コード

ArrayListだけではなく、親クラスと、親の親クラスもクラス図にしました。

※こうしないと、全体がみえくかったです。

2.9.1.1. パッケージ図

../_images/java.util.ArrayList.image001.jpg

まず、パッケージ図はこれです。

ArrayListはutil内にあり、関連するpackageはこんな感じでした。

2.9.1.2. クラス図

クラスです。java.util.ArrayList

../_images/java.util.ArrayList.svg

親クラスです。java.util.AbstractList

../_images/java.util.AbstractList.svg

親の親クラスです。 java.util.AbstractCollection

../_images/java.util.AbstractCollection.svg

元の図は大きいので小さく切り取って、見ていきます。

2.9.2. 継承

2.9.2.1. ArrayList。ターゲットのクラス。

../_images/java.util.ArrayList.image002.jpg

2.9.2.1.1. AbstarctListを継承しています。

ですので、このクラスの処理を流用して処理を共通化する設計になっています。

2.9.2.1.2. RandomAccessを実現しています。

ランダムアクセスできるよ、という目印になります。

配列のように添え字(例a[1]、a[2])で直接データにアクセスできるようなものをいいます。

※そうじゃないものもあります、例えば前から順番にしかアクセスできないとか。

2.9.2.1.3. Listを実現しています。

ListはList関連のクラスの共通インタフェースになっています。

2.9.2.1.4. Serializableを実現しています。

これを実現するとシリアライズできるよと、という意味になります。

2.9.2.2. 親クラスのAbstractList

../_images/java.util.ArrayList.image003.jpg

2.9.2.2.1. まずはListを実現しています。

List関連のクラスはこれを実現することになっています。

2.9.2.2.2. AbstractCollectionを継承しています。

Collectionというのは、Listよりももっと幅の広い概念で、集めたもの集合のような意味になります。

なので、この意味に共通する処理を共有しています。つまりAbstractCollectionの処理を流用しています。

2.9.2.3. 親の親クラスAbstractCollection

../_images/java.util.ArrayList.image004.jpg

2.9.2.3.1. Collectionを実現していますね。

Collection関連クラスはこのインタフェースを実現することになっています。

2.9.3. フィールドを見てみる

2.9.3.1. ArrayList。ターゲットのクラス

../_images/java.util.ArrayList.image005.jpg

2.9.3.1.1. serialVersionUID

-serialVersionUID:long=8683452581122892189L{readOnly}

シリアライズ用のIDでシリアライズするときに必要なものです。

2.9.3.1.2. DEFAULT_CAPACITY

-DEFAULT_CAPACITY:int=10{readOnly}

デファオルトで確保する配列サイズです。このサイズで領域を増やしていきます。

2.9.3.1.3. EMPTY_ELEMENTDATA

-EMPTY_ELEMENTDATA:Object[]={}{readOnly}

サイズを0で指定してインスタンスを生成した時は配列にこれが使われます。

2.9.3.1.4. DEFAULTCAPACITY_EMPTY_ELEMENTDATA

-DEFAULTCAPACITY_EMPTY_ELEMENTDATA:Object[]={}{readOnly}

サイズを指定しないでインスタンスを生成した時などにこの配列がセットされます。

2.9.3.1.5. elementData

~elementData:Object[]{transient}

これに配列データが格納されます。

2.9.3.1.6. size

-size:int

ArrayListの要素の個数を格納します。

2.9.3.1.7. MAX_ARRAY_SIZE

-MAX_ARRAY_SIZE:int=Integer.MAX_VALUE-8{readOnly}

ArrayListに格納できる配列の最大サイズです。

2.9.3.2. 親クラスのAbstractList

../_images/java.util.ArrayList.image006.jpg

2.9.3.2.1. modCount

#modCount:int=0{transient}

modification Countの略で、削除とか挿入とかを行うたびにmodCountをカウントアップした値をセットしていました。

これは何に使っているんだろうと、調べてみました。

元のリストから部分リストを取得したり、Iteratorを取得して操作するときに使っていました。

元のリストのmodCountと違いが無いかチェックして違うと例外を発生させます。

つまり、元リストと部分リストなどとの整合性が取れなくなっていないかをチェックするためのモノのようでした。

2.9.3.3. 親の親クラスAbstractCollection

../_images/java.util.ArrayList.image007.jpg

2.9.3.3.1. MAX_ARRAY_SIZE

-MAX_ARRAY_SIZE:int=Integer.MAX_VALUE-8{readOnly}

AbstractCollectionに格納できる配列の最大数。

2.9.4. メソッドを眺めてみます

2.9.4.1. ArrayList。ターゲットのクラス

2.9.4.1.1. コンストラクタ

+ArrayList(initialCapacity:int):void
+ArrayList():void
+ArrayList(c:Collection<?extends E>):void

この辺はコンストラクタです。

引数がCollectionのモノは中で配列を生成しいてコピーしています。

2.9.4.1.2. trimToSize

+trimToSize():void

格納されているサイズに合わせて、キャパシティを刈り込んで(trim)いました。

メモリーの有効活用ということでしょう。

※Arrays.copyOfを使ってtrimしています。

2.9.4.1.3. ensureCapacity

+ensureCapacity(minCapacity:int):void
-ensureCapacityInternal(minCapacity:int):void
-ensureExplicitCapacity(minCapacity:int):void
-grow(minCapacity:int):void
-hugeCapacity(minCapacity:int):int

引数の数値をもとに、キャパシティを確保(ensure)する処理です。

※あらかじめキャパシティを大きくとっておきたいときに使うのでしょう。

2.9.4.1.4. size

+size():int
+isEmpty():boolean
+contains(o:Object):boolean

格納している要素の数を取得する関連。

containsは引数のモノが含まれているか確認する機能です。

2.9.4.1.5. indexOf

+indexOf(o:Object):int
+lastIndexOf(o:Object):in

indexOfは配列の先頭から検索して、その位置を返す。

lastIndexOfは配列の後ろから検索して、その位置を返す。

2.9.4.1.6. clone

+clone():Object

自分のクローン(コピー)を作ります。

2.9.4.1.7. toArray

+toArray():Object<T>
+toArray(a:T[]):T

内部の配列をコピーして返します。

2.9.4.1.8. get

~elementData(index:int):E
+get(index:int):E

引数の位置の要素を取得します。

2.9.4.1.9. set

+set(index:int,element:E):E

引数の位置の要素をセットします。

2.9.4.1.10. add

+add(e:E):boolean
+add(index:int,element:E):void

末尾に追加したり、引数indexの位置に要素を挿入したりできます。

2.9.4.1.11. remove

+remove(index:int):E
+remove(o:Object):boolean
-fastRemove(index:int):void
+clear():void

removeは引数で指定した位置やモノを削除します。

fastRemoveはremoveの中で使われています。

配列コピーのSystem.arraycopyを使って実装しています。fastとついているので高速なのでしょうか。

clearは配列をサイズ分forループで回してnullで埋めます。

2.9.4.1.12. addAll

+addAll(c:Collection<?extends E>):boolean
+addAll(index:int,c:Collection<?extends E>):boolean

Collection集合をまとめて、追加することができます。 配列コピーのSystem.arraycopyを使っています。

2.9.4.1.13. removeRange

#removeRange(fromIndex:int,toIndex:int):void
-rangeCheck(index:int):void
-rangeCheckForAdd(index:int):void
-outOfBoundsMsg(index:int):String
+removeAll(c:Collection<?>):boolean

removeRangeは指定された範囲の内部配列を削除します。

rangeCheckは引数indexが範囲内かチェックをします。範囲外ならIndexOutOfBoundsException例外発生。

outOfBoundsMsgはrangeCheckの中で使われていて、例外メッセージ文字列を作っています。

2.9.4.1.14. retainAll

+retainAll(c:Collection<?>):boolean
-batchRemove(c:Collection<?>,complement:boolean):boolean

retainAllは引数の集合のものだけは保持(retain)して、それ以外は削除します。

batchRemoveはretainAllの中で使われています。

2.9.4.1.15. writeObject

-writeObject(s:java.io.ObjectOutputStream):void
-readObject(s:java.io.ObjectInputStream):void

これは、シリアライズに使う処理です。

引数Streamに内部配列を書き込んだり、読み込んだりします。

2.9.4.1.16. iterator

+listIterator(index:int):ListIterator<E>
+listIterator():ListIterator<E>
+iterator():Iterator<E>

イテレーターとして、内部配列を取得します。

イテレーターの種類によって、アクセスだけできるとか、編集できるとか違いはあります。

処理は内部クラスのListItr、Itrで行っています。

2.9.4.1.17. subList

+subList(fromIndex:int,toIndex:int):List<E>
~subListRangeCheck(fromIndex:int,toIndex:int,size:int):void

指定した範囲の一部分(sub)のListを返却します。

全体ではなく部分だけ欲しいときに使います。

処理は内部クラスのSubListで行っています。

2.9.4.1.18. forEach

+forEach(action:Consumer<?super E>):void

戻り値のない関数型インタフェースを引数にセットすると、for eachのようにforループを実行する。

つまりはfor構文ではなく、メソッドでfor eachを再現したものです。

※Consumerのvoid accept(T t)をオーバーライドします。

2.9.4.1.19. spliterator

+spliterator():Spliterator<E>

イテレーターの一種である、スプリッテレーターを返します。

trySplit()メソッドと使うとListを簡単に分割することができて、並列処理に便利なようです。

※これは使ったことが無いので、いまいち用途が分かりにくいのですが...。

2.9.4.1.20. removeIf

+removeIf(filter:Predicate<?super E>):boolean

引数filterにセットした関数クラスの条件に合った要素を削除します。

※Predicateのboolean test(T t)をオーバーライドします。

2.9.4.1.21. replaceAll

+replaceAll(operator:UnaryOperator<E>):void

引数operatorにセットした関数クラスの処理により、要素の内容を置き換えます。

※UnaryOperatorの親クラスのFunction のR apply(T t);をオーバーライドします。

2.9.4.1.22. sort

+sort(c:Comparator<?super E>):void

引数cにセットした関数クラスの比較処理により、要素の並べ替えをします。

※Comparatorのint compare(T o1, T o2)をオーバーライドします。

2.9.4.2. 親クラスのAbstractList

2.9.4.2.1. コンストラクタ

#AbstractList():void

これはコンストラクタです。処理は空っぽです。

2.9.4.2.2. add

+add(e:E):boolean

「+add(index:int,element:E):void」を中で読んでいるだけです。 ※add(size(), e); 配列の末尾に引数eを追加します。

2.9.4.2.3. get、set

+get(index:int):E
+set(index:int,element:E):E

引数indexの位置の要素を取得したり、設定したりします。

※ただ、このメソッドはオーバーライドして使われることを想定しているようです。

※呼ぶとUnsupportedOperationExceptionの例外が発生します。

2.9.4.2.4. add

+add(index:int,element:E):void

引数indexの位置に要素を挿入します。

※ただ、このメソッドはオーバーライドして使われることを想定しているようです。

※呼ぶとUnsupportedOperationExceptionの例外が発生します。

2.9.4.2.5. remove

+remove(index:int):E

引数indexの位置に要素を削除します。

※ただ、このメソッドはオーバーライドして使われることを想定しているようです。

※呼ぶとUnsupportedOperationExceptionの例外が発生します。

2.9.4.2.6. indexOf

+indexOf(o:Object):int
+lastIndexOf(o:Object):int

前方検索・後方検索検索して引数oがあったら、その位置を返します。

2.9.4.2.7. clear

+clear():void

配列要素をすべて削除します。

「#removeRange(fromIndex:int,toIndex:int):void」を読んでいるだけです。

※removeRange(0, size());

2.9.4.2.8. addAll

+addAll(index:int,c:Collection<?extends E>):boolean

index位置に引数cのCollection集合を挿入します。

「+add(index:int,element:E):void」を使って実装しています。

2.9.4.2.9. iterator

+iterator():Iterator<E>
+listIterator():ListIterator<E>
+listIterator(index:int{readOnly}):ListIterator<E>

内部クラスのItr、ListItrを生成して返却しています。

※上からこんな感じです。

「return new Itr();」

「return listIterator(0);」

「return new ListItr(index);」

2.9.4.2.10. subList

+subList(fromIndex:int,toIndex:int):List<E>

配列位置がfromIndexからtoIndexの範囲の部分リスト返します。

返すクラスは子クラスのRandomAccessSubListまたはSubListとなっています。

RandomAccessを実現しているどうかで処理を分けています。

2.9.4.2.11. equals

+equals(o:Object):boolean

内部のListと引数oのListを比較します。

2.9.4.2.12. hashCode

+hashCode():int

内部のListからハッシュ値を計算します。

2.9.4.2.13. removeRange

#removeRange(fromIndex:int,toIndex:int):void

引数の範囲の要素を削除します。

2.9.4.2.14. rangeCheckForAdd

-rangeCheckForAdd(index:int):void
-outOfBoundsMsg(index:int):String

indexがListの範囲内にあるかチェックします。

また、indexが範囲外あるときのメッセージ文字列を作成します。

2.9.4.3. 親の親クラスAbstractCollection

2.9.4.3.1. コンストラクタ

#AbstractCollection():void

このへんは、コンストラクタです。処理はありません。

2.9.4.3.2. iterator

+iterator():Iterator<E>

これは、イテレーターの取得です。オーバーライドします。

2.9.4.3.3. size

+size():int
+isEmpty():boolean

サイズ(配列の要素数)を取得したりします。

sizeはオーバーライドしなければいけません。

isEmptyはsize()をコールして0だったらtrueを返しているだけです。

2.9.4.3.4. contains

+contains(o:Object):boolean

引数がoが配列内に含まれているかチェックします。

2.9.4.3.5. toArray

+toArray():Object[]
<T>+toArray(a:T[]):T[]
<T>-finishToArray(r:T[],it:Iterator<?>):T[]
-hugeCapacity(minCapacity:int):int

toArray内部配列の要素をコピーして返却する処理です。

つまり、Collectionから配列に変換しています。

引数a:T[]はサイズが足りていれば、これに配列コピーして戻り値として返します。

足りないときにはjava.lang.reflect.Array.newInstanceで新規に作った配列にコピーして返します。

2.9.4.3.6. add

+add(e:E):boolean

これは、常に例外UnsupportedOperationExceptionを投げます。

自分でオーバーライドしろということでしょう。

2.9.4.3.7. remove

+remove(o:Object):boolean

内部配列内に引数o:Objectを探して、有れば最初の一つを削除します。

2.9.4.3.8. containsAll

+containsAll(c:Collection<?>):boolean

引数cの集合が内部配列にあるかチェックします。

2.9.4.3.9. addAll

+addAll(c:Collection<?extends E>):boolean

引数の集合cを内部配列に追加します。

2.9.4.3.10. removeAll

+removeAll(c:Collection<?>):boolean

引数cの集合を内部配列からすべて削除しています。

2.9.4.3.11. retainAll

+retainAll(c:Collection<?>):boolean

ちょうど、removeAllと逆の仕様になっています。

引数cの集合を内部配列に確保(retain)し、それ以外はすべて削除しています。

2.9.4.3.12. clear

+clear():void

内部配列を消し(clear)て空にします。

2.9.4.3.13. toString

+toString():String

内部の要素を文字列として吐き出します。

例:"[1 ,2 ,3 ,4]"

2.9.5. 内部クラス

2.9.5.1. Itr。イテレーターを作ります。

../_images/java.util.ArrayList.image008.jpg

ArrayListクラスのメソッド「+iterator():Iterator<E>」内で生成して返されます。

※return new Itr();

ArrayListのイテレーター処理に使われます。

2.9.5.2. ListItr。Listイテレーターを作ります。

../_images/java.util.ArrayList.image009.jpg

メソッド「+listIterator(index:int):ListIterator<E>」と、「+listIterator():ListIterator<E>」で生成されて返却されます。

ArrayListのListイテレーター処理に使われます。

※編集できるイテレーターです。

2.9.5.3. SubList。部分リストを作ります。

../_images/java.util.ArrayList.image010.jpg

ArrayListから一部を切り出すために作られたクラスです。

ArrayListのメソッド「+subList(fromIndex:int,toIndex:int):List<E>」の中で生成されて返却されています。

※return new SubList(this, 0, fromIndex, toIndex);

2.9.5.4. ArrayListSpliterator。スプリッテレーターを作ります。

../_images/java.util.ArrayList.image011.jpg

ArayListのイテレーターの一種である、スプリテレーターの処理をするために作られたクラスです。

ArrayListの「+spliterator():Spliterator<E>」メソッドで生成されて返却されます。

※return new ArrayListSpliterator<E>(ArrayList.this, offset, offset + this.size, this.modCount);

2.9.6. まとめ

ArrayListは配列を便利に利用できるようにしたクラスです。

これだけの機能がついて、かなり使いでのあるクラスになっています。

コードを調査していて思ったのが、これ内部クラスの使い方がうまいとなあと感じます。

クラス内の処理が複雑になってきた、内部クラスに処理を切り分けるときれいにまとめられるんですね。