アウトライン
ArrayListには、Listインタフェースの実装で、Javaは1つのクラスを達成するための最も一般的に使用されるコンテナである、それは理解できる「変数配列」です。
私たちは、あなたがJava配列の初期化の長さを指定する必要がある場合ということを知っているが、指定を変更することはできません。内部のArrayListは、それが作られた拡張機能のアレイを備え、アレイである:主膨張のArrayListのコアである容器の増加、動的要素です。
フロント「JDKのソースコード解析-リスト、イテレータ、反復子には」Listインタフェース、基本的に同じリストのArrayListのメインメソッドの方法を概説しているので、この解析は、その内部構造と膨張の原理に焦点を当てています。
以下のArrayList継承構造(インターフェースの一部ではありません)。
コンストラクタ
私たちは分析を続行するには、コンストラクタで始めます。それぞれのArrayList 3つのコンストラクタ、:
1.引数なしのコンストラクタに
/ ** * 10の初期容量で空のリストを作成します。 * / 公共のArrayList(){ この .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA。 }
elementDataとDEFAULTCAPACITY_EMPTY_ELEMENTDATA:コンストラクタは、2つの変数が含まれます。次のようにこれらの2つの変数が定義されています。
過渡オブジェクト[]からelementData。// 非プライベート入れ子になったクラスのアクセスを簡単にするために、
elementDataアレイは、データを格納するための容器としてのArrayListである配列の種類を見ることができるオブジェクト。
/ ** デフォルトサイズの空のインスタンスで使用*共有空の配列インスタンス。私たちは 時に膨張するためにどのくらい知っているEMPTY_ELEMENTDATAと区別* *最初の要素が追加されます。 * / プライベート 静的 最終オブジェクト[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
DEFAULTCAPACITY_EMPTY_ELEMENTDATAは、Object型の空の配列です。したがって、引数なしのコンストラクタの役割はからelementDataにObject型の空の配列に初期化されます。
2.初期容量のコンストラクタを指定します
次のようにパラメータを渡すこの構成は、すなわちInitialCapacityの値は、アレイの内部容量を初期化します。
公共のArrayList(int型InitialCapacityの値){ 場合(InitialCapacityの値> 0 ){ この .elementData = 新しいオブジェクト[InitialCapacityの値]。 } そう であれば(InitialCapacityの値== 0 ){ この .elementData = EMPTY_ELEMENTDATA。 } 他{ スロー 新しい例外:IllegalArgumentException( "不正な容量:" + InitialCapacityの値を)。 } }
配列変数ストレージからelementData要素を初期化するための着信初期容量(InitialCapacityの値)に応じて、この構成。初期容量が0の場合、からelementDataはEMPTY_ELEMENTDATA、以下の変数に初期化されます。
プライベート 静的 最終オブジェクト[] EMPTY_ELEMENTDATA = {};
DEFAULTCAPACITY_EMPTY_ELEMENTDATAの配列は、後者が異なる場合に拡張するための指定されたArrayListの初期容量かを区別するために空のオブジェクト配列、2つの異なる名前です。
3.コンストラクタを指定のセットを初期化
これは、以下のような構成では、すなわち、要素のArrayListオブジェクトで初期化されたコレクションを使用して、着信コレクションのセットです。
公共のArrayList(<?コレクション延び E> C){ からelementData = c.toArray()。 もし((サイズ= elementData.length)!= 0 ){ // c.toArrayが(間違って)オブジェクト[](6260652を参照)を返さない可能性があります 場合は(elementData.getClass()!=オブジェクト[]。クラス) からelementData =配列.copyOf(からelementData、サイズ、オブジェクト[]。クラス)。 } 他{ // 空の配列と交換してください。 この .elementData = EMPTY_ELEMENTDATA。 } }
注意:コレクションが空の場合、これはNPEがスローされます、空の前に文を初期化する必要があります。
原則拡大
これは、ArrayListのコンストラクタの上に分析し、どのようにそれを動的に拡張をArrayListのですか?
私たちは、add()分析のための方法から開始することができ、以下のように、(のaddAll()メソッドを別々に分析ではない、似ています):
// (要素の数を含む)サイズのArrayList プライベート int型のサイズ; // リストの最後に、指定された要素 のパブリック ブールの追加(E E){ ensureCapacityInternal(サイズ + 1); // インクリメントModCount !! [サイズのからelementData ++ ] = E; 返す trueに; }
これは、()メソッドは、実行が最初ensureCapacityInternal()メソッドを実行するアドインに見ることができます。
プライベート 無効 ensureCapacityInternal(int型minCapacityに){ ensureExplicitCapacity(calculateCapacity(からelementData、minCapacityに)); }
この方法は、第一calculateCapacity minCapacityにアレイ法によって必要な容量を算出し、(成長実行するかどうかを決定する)方法:
// デフォルトの初期容量 のプライベート 静的 最終 int型 DEFAULT_CAPACITY = 10 ; プライベート 静的 INT calculateCapacity(オブジェクト[]、のからelementData intはminCapacityにすること){ // ここでの唯一の引数なしで初期化コンストラクタの使用において、(最初に実行し、メソッドを追加容量)は10に初期化される IFの(==からelementData {DEFAULTCAPACITY_EMPTY_ELEMENTDATA) を返す; minCapacityにすること、DEFAULT_CAPACITY(Math.max)を } リターンminCapacityにすること; } プライベート ボイド ensureExplicitCapacity(INT minCapacityにすること){ ModCount ++ ; //オーバーフロー意識コード 場合(minCapacityに- elementData.length> 0 ) 成長(minCapacityに)。 }
ここにいることが分かる、初期値からelementData DEFAULTCAPACITY_EMPTY_ELEMENTDATA場合、すなわち、10のデフォルトの初期容量を引数なしでArrayListのコンストラクタを使用して初期化。
容量は、元の配列minCapacityに所望の長さのサイズよりも大きい場合(すなわち、配列の元の長さが十分ではありません)が行わグロウ()メソッド(膨張配列)です。
プライベート ボイドは(成長int型minCapacityにする){ // オーバーフローを意識したコード int型 oldCapacity = elementData.length。 INT newCapacity = oldCapacity +(oldCapacity >> 1 )。 もし(newCapacity - minCapacityに<0 ) newCapacity = minCapacityに。 もし(newCapacity - MAX_ARRAY_SIZE> 0 ) newCapacity = hugeCapacity(minCapacityに)。 // minCapacityには、サイズに通常近いので、これは勝利です: からelementData = Arrays.copyOf(からelementData、newCapacity)。 } プライベート 静的 INT hugeCapacity(INT minCapacityに){ 場合(minCapacityに<0)// オーバーフロー スロー 新しい(OutOfMemoryErrorが発生します)。 リターン(minCapacityに> MAX_ARRAY_SIZE)? Integer.MAX_VALUEの: MAX_ARRAY_SIZE。 }
新しいコンピューティング容量サイズ:
INT newCapacity = oldCapacity +(oldCapacity >> 1)。
新しい容量が1.5倍の体積で、見ることができ、膨張が1.5倍された後、まだ、必要な容量の直接使用を所望の容量に達していない場合。
どのようにそれの拡大?使用Arrays.copyOf()メソッドは、新しいアレイ、新しい配列にコピー元の配列の要素を作成します。
elementData = Arrays.copyOf(からelementData、newCapacity)。
追跡方法は、最終的System.arraycopyの()メソッドと呼ばれる、見出すことができます。
公共の 静的な ネイティブ 無効のarraycopy(オブジェクトSRC、 int型 srcPos、 オブジェクトdestが、int型 destPos、int型の長さ)。
ArrayListの展開の概要
1.なし初期容量の場合
容量までの要素を追加する前には膨張が10に等しくない最初の実行add()メソッドは、デフォルトの初期化のアレイ10の長さは、最初の要素11、15(10 + 10>の膨張容量を追加する場合> 1)、というように。
2.指定された初期容量であればInitialCapacityの値
容量が等しいInitialCapacityの値である場合、配列InitialCapacityの値の容量に達する前に、膨張がより添加元素場合、実行されていない、拡張が行われ、上記拡張操作です。
3.新しい容量サイズ
それらは、所望の膨張容積を所望の大きさに達していない場合(のaddAll()メソッドを使用するときに起こり得る); 1.5倍アレイ容量の拡張容量後のデフォルトのアレイ。
スレッドの安全性
これは単にスレッドセーフとして理解することができる:複数のスレッドが同時にメソッドまたは変数、何の問題を動作させるステップと、問題が発生した場合、スレッドセーフであると考えることができます。
ArrayListのは、主に二つありますが、スレッドセーフではありません。
データのArrayList(時間拡張)を追加する複数のスレッドは、配列の境界例外(ArrayIndexOutOfBoundsExceptionが)を生成することができる1。
同じArrayListのを横断2.複数のスレッドが、それを修正するためのスレッドがある場合に、ConcurrentModificationExceptionをスローすることがあります。
最初の分析のために()メソッドを追加します。
パブリック ブール追加(E、E){ ensureCapacityInternal(サイズ + 1)。 // インクリメントmodCount !! elementData [サイズ++] = E。 返す 真; }
注意:私は、オペレーションが非アトミックです++。
シーンの分析:
もし第二の要素が追加された初期容量のArrayList 1、スレッドT1およびT2同時に添加元素(add()メソッド)それ、拡張の必要性。
実行タイミングであれば、このとき:
1. T1、T2検出能力が必要とされます
T2再度実行ensureCapacityInternal後()メソッドは、この場合、T1及びT2はelementData.length = 1、サイズ= 1、T1もし実行ensureCapacityInternal()拡張の方法の前に、elementData.length = 2、サイズ= 1を持ってしています場合、初期サイズ= 1、T1拡張elementData.length = 2であるため、その後の膨張のでT2(全く実行が成長しない()メソッド)ではありません。
2. T1は、割り当てとサイズ++操作を行います
割り当てT1からelementData [1] = XXおよびサイズ++を行った後、サイズの増分は2です。
3. T2は、割り当て操作(配列境界)動作及びサイズを行い++
ステップサイズ++ T1操作が行われたので、現在のサイズ= 2、時間割当からelementData [サイズが++]からelementData [2]の割り当てを実行し、= 2 elementData.length、1の最大屈折率は、その配列が発生します境界例外(ArrayIndexOutOfBoundsExceptionが)。
シーン解析II:
ArrayListの、そのトラバーススレッドT1があり、T2スレッドは、それらを横断し、要素の一部を除去します。
ArrayListのメソッドの例をイテレータする横断するとき、次のように、コードは次のとおりです。
公共イテレータ<E> 反復子(){ 返す 新しい)(のITR; }
以下のように、内部クラスのITR作成します。
プライベート クラス ITRが実装するIterator <E> { int型 expectedModCount = modCountを。 // ... 公共E次の(){ checkForComodification(); int型私= カーソル。 もし(I> = サイズ) スロー 新しいはNoSuchElementExceptionを(); []からelementDataオブジェクト = ArrayListのを。この.elementData; もし(I> = elementData.length) スロー 新しい)(ConcurrentModificationExceptionを。 カーソル = I + 1; 戻り [lastRet =の(E)からelementData ; I] } // 他のスレッド構造変更かどうかをチェック 決勝 無効checkForComodification(){ IF(ModCount =!ExpectedModCount) スロー 新しい新)はConcurrentModificationException(; } }
そして、ArrayListのは)(追加、削除()操作などは、構造的な変更modCountの++の原因となります。従って:スレッドT1のみのArrayListトラバースされる場合、実行同じ要素のArrayList除去操作にスレッドT2、modCountの値を変更し、スレッドT1をもたらすことConcurrentModificationExceptionをトリガする、modCount = expectedModCountあります!
ArrayListの概要
1. ArrayListには、「アレイの自動拡張」として理解することができ、デフォルトの初期容量が拡張が1.5倍容量であるたびに、デフォルトでは、10です。
2.拡張は、アレイの前に新しい配列要素を作成し、新しい(したがって、ArrayListの中への元素の既知の数に、初期化時間で指定された長さの拡大を回避することができる)にコピー。
3.安全なスレッドとマルチスレッドのシナリオでの使用には適していません。
渇望する愚か者であれ。
PS:この記事では、最初の公開のマイクロチャネル番号から登場しました。