ハードコアArrayListのソースコード解析は、私は何が良い一日以上行くことを約束します

序文

知識を求める人は、現在のシリーズjdk1.8学習分析に基づいています。Benpianソースコード解析は、すべてのメソッドが最後に多額の後に分析されるすべてのステップの分析を行うことになる。あなたが最終面接限り、ソースコード解析ステップが気に入らない場合読者は、ライン上のテキストの最後に結論の要約を見ることができます。

サポート技術情報シーカー(技術知識を広める、オープンソースの精神を継承;)

二つのArrayListのソースコード解析

2.2リファレンスヌルコンストラクタソース解析

デバッグコード

  public static void main(String[] args) {
        // 初始化长度为0
        ArrayList list = new ArrayList();//断点
  }      

;ブレークポイントに転送し始めてまずだけで、デフォルトの表示は、ArrayListの長さがゼロで初期化されます

第二には、コンストラクタを入力してください

   public ArrayList() {
        // elementData 是 Object[] ---> 故 Arraylist底层由对象数组实现
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

DEFAULTCAPACITY_EMPTY_ELEMENTDATAがからelementDataに割り当てられていることがわかります。

DEFAULTCAPACITY_EMPTY_ELEMENTDATA、以下のソースコードが何であるかを見てみましょう、あなたは最終的に変更されたオブジェクト[]配列を見ることができます。

   // 共享的空数组实例,当第一个元素添加时从EMPTY_ELEMENTDATA辨别出扩张大小
   private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

次のようにソースコードが何であるかでからelementDataの外観

    // 动态数组缓冲区,ArrayList存储元素的地方,elementData长度就是ArrayList长度
    // 当第一个元素添加时 DEFAULT_CAPACITY 被被扩张        
    transient Object[] elementData; // non-private to simplify nested class access

再びデバッガにどのような親クラス張ArrayListの継承を確認するために、クラスダイアグラムに親クラスを入力します

次の順序で真のENTERED

AbstractList - > AbstractCollection - >オブジェクト

興味深いことにまた、次のコード行をリリースし、分析時の知識の探求者は、予期せぬ変化にConcurrentModificationExceptionが例外をスローする場合は、レコードのサイズを変更するには、リストの数であること、実行され、これはなぜイテレータを説明しますリストには、他の操作を除去するために使用することができない(ここで関連するソースは、後で分析し、反復子反復子を延長しない);これは、フェイルファスト機構の実施形態によって提供されます。

科学フェイルファストメカニズム

フェイルファスト故障メカニズムが決意を作るために失敗したことを検出するための迅速、設定された送信構造にConcurrentModificationException例外がスローされますバグを変更する際に、ある高速なメカニズム、です。

// 记录list被修改的次数,也就是大小改变的次数
// 如果在迭代器中使用迭代,做出了不是期待的改变就会抛出ConcurrentModificationException
protected transient int modCount = 0;

TIP1:リストAは後に何が起こるか反復プロセスで収集方法を削除するには、コール後のインタビュー?A:私は、知識のArrayListのソースシリーズの求職者を見てきましたが、フェイルファストするメカニズムは、構造変化リストにConcurrentModificationExceptionが発生したことを検知によるものである例外をスローし、背の高い木がある瞬間、私はコレクションに起因する答えることができ、反復プロセスで行われました、次のセット、以前の操作を追加し、削除しますか?それの詳細すぎるが、それが低すぎる、インタビュアーの強さは、あなたがレベルアップ知って、良い若い男の背中の量!

ヒント:ArrayListの基礎となる実装動的オブジェクトの配列

2.2初期容量のソースコード解析

与えられた初期値のサイズに対応するオブジェクトアレイ作成され、一般の初期容量をArrayListに、そうでなければ不正な引数例外がスローされる、さもなければ、初期値が与えられていない、メンバーは、空のオブジェクトの配列を横断共用しました。

テストコード

public static void main(String[] args) {
        // 初始化为0
        ArrayList list = new ArrayList(5);
    }

ソース

    public ArrayList(int initialCapacity) {
        // 初始化容量大于0,创建指定大小的object数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        // 初始化容量等于0,空 object数组
        } else if (initialCapacity == 0) {
            // 其中 private static final Object[] EMPTY_ELEMENTDATA = {};
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            // 否则抛出非法参数异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

指定されたサイズのArrayListのArrayListを初期化するために与えられ、指定したサイズが戻るの先端容量

2.3追加源分析法

テストコード

   public static void main(String[] args) {
        // 初始化为0
        ArrayList list = new ArrayList();
        list.add("a");
        System.out.println(list);
    }

まず:アドオンソースを見てみましょう

  public boolean add(E e) {
        // 确保容量
        // size就是list的大小,也就是集包含合的元素个数
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

見; ensureCapacityInternal(サイズ+ 1)容量はアレイの元の長さに1を加算し、この発現結論から直接導出することができることを確実にする方法が分かります

    private void ensureCapacityInternal(int minCapacity) { 
    		// 期望大小 添加1个a 所以是1
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 
        	// 判定是否是空数组
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 
            // 在1 和 10(DEFAULT_CAPACITY)中取最大值
        }

        ensureExplicitCapacity(minCapacity);//此时就是10
    }

直接、元素の添加によるものが1である場合、それはその時点でソース10のデバッギング、初期容量の後に既定の最大値と比較したとき、1の容量のために望ましいと結論付けることができるのでArrayListの要素を追加した後、所望の拡張サイズは10に拡大します

私たちは、その具体的な拡張に加え、変更番号1のコレクションを確保するために、ArrayListの長さより大きく、必要に応じて拡張大きい、再び拡大になります見て。

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//list改变次数加1

        // overflow-conscious code
        //如果期望扩容小于元素ArrayList长度,会进行再次扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);//扩容
    }

ルック膨張grow(minCapacity);その内部実装も非常にシンプルなアイデアであり、各拡張は、元の長さのリストに50%を加え、所望の配列のサイズが最大長さ(最大整数値int - 8)を超える最大の整数値intとして配列長の使用に;最後に、配列がコピーされ、アレイのサイズを再指定し、アロケーション長は、アレイの最大長を超える場合は、値を保持する一方が、プレースホルダヘッドとして数8位アレイでMAX_ARRAY_SIZEのOutOfMemoryErrorがスローされるだろう、; VMは、一般にこれはしないかもしれない練習のサイズを制限しますMAX_ARRAY_SIZEint型の最大値に言及し、異常の最大のではなく投げます。

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//oldCapacity=0 当前元素a未插入时的长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity=0 原始长度 + 原始长度左移一位,也就是 除以2; 如果长度10,经过扩容后就是15
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;//(0-10)< 0 故 新的容量为10;即 newCapacity=10
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);///如果期望容量大于数组容量最大值就使用Int最大值;否则使用数组最大值
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);//数组拷贝,容量为10
    }

hugeCapacity(minCapacityに);ソースは、次の

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

ヒント:ArrayListの初期容量素子10を追加する拡張、0であり、10以上の後に長さ当たりのプラス50%の配列の元の長さの膨張; ArrayListのInteger.MAX_VALUEの最大長 - 8;最終的とは、配列を呼び出します。 copyOfメソッドは、オブジェクトの配列を再割り当て

2.4集合ソースコード解析

テストコード

    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("a");
        list.set(0,"b");
        System.out.println(list);
    }

setメソッドを入力するにはまず、

   public E set(int index, E element) {
        rangeCheck(index);// index =0 ; element = b ;检测ArrayList是否越界

        E oldValue = elementData(index);//获取原来数组索引对象
        elementData[index] = element;//根据对应重新赋值
        return oldValue;//返回对象
    }

最初の行は、国境を越えたかどうかを検出することであるのArrayList

  private void rangeCheck(int index) {
        if (index >= size) // 给定索引大于等于数组长度抛出异常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

2行目は、元のオブジェクトのオブジェクトのアレイ位置を取得します。

  @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];// index =0 ; elementData 是个 object[]
    }

3行目はインデックス戻るに従って再割り当て; OLDVALUEがので、ここからelementDataある[インデックス]。

ヒント:割り当ての設定値は、再配列の位置にあり、更新操作の等価;所与、例外をスローする場合は全く要素位置は存在しません。

2.5 GETソースコード解析

テストコード

    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("a");
        list.get(0);
        System.out.println(list);
    }

getメソッドに

  public E get(int index) {
        rangeCheck(index);//检测数组是否越界

        return elementData(index);//获取数组对应位置上的对象
    }

メソッドセットバック、またはコールからelementDataと一致対応する位置上のオブジェクトの配列を取得し、取得何も奇妙な点法。

2.6削除ソースコード解析

テストコード

    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("a");
        list.add("b");
        list.remove(0);
        System.out.println(list);
    }

全体としてソース

    public E remove(int index) {
        rangeCheck(index);// 检测数组是否越界

        modCount++;//修改次数 +1
        E oldValue = elementData(index);//获取数组index位置上的值 oldValue=a

        int numMoved = size - index - 1;// numMoved = 2-0-1 = 1;表示移动的数量
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);//数组拷贝
        elementData[--size] = null; // clear to let GC do its work.数组长度 -1 位置置为null.

        return oldValue;//返回移除的值
    }

ここでは方法の簡単な説明することができのarraycopy

public static native void arraycopy(Object src,int  srcPos,Object dest, int destPos,int length);

パラメータ

  • SRC配列をコピーします
  • srcPosは、インデックスのコピーを開始します
  • DESTターゲット配列
  • destPosは、位置データを格納を開始します
  • 番号の長さが配置されています

そして、人々はまだ混乱しなければなりません理解していない、我々は見ての栗を与えます

    public static void main(String[] args) {
        int[] src = new int[]{1,2,3,4,5};
        int[] desc = new int[]{6,7,8,9,10};
        System.arraycopy(src,2,desc,1,2);
        Arrays.stream(desc).boxed().forEach(des ->{
            System.out.println(des); // 6 ,3, 4, 9, 10
        });
    }

SRCインデックス= 2が位置値から始まり、それはいくつかのテイク長さ= 2(.E 3,4。)かかり、開始位置がDESCインデックス= 1配置され、この時点で終了するが覆われている(3 、4つのオーバーライド7,8)

いくつかのコピー;あなたは、インデックス= 0(すなわち、要素1)を削除すると仮定すると、srcとDESCが一緒に配列されている原則は、その後だけで、ソースコード内で、毛布を理解しているのですか?movNum =サイズ - インデックス-1 = 5 - 0〜1 = 4;コピー4; 2,3,4,5-オーバーライドSRC 1,2,3,4-、最終的に2を得、4、5 5;そして最後の最後の要素が削除操作を完了するために、nullに設定されています!ああ、あなたはそれを学ぶ、知識の探求者が行っているデータ構造を書いた後、背の高い、まだ、瞬間のことを学びました。

    public static void main(String[] args) {
        int[] src = new int[]{1,2,3,4,5};
        int[] desc = new int[]{1,2,3,4,5};
        System.arraycopy(src,1,desc,0,4);
        Arrays.stream(desc).boxed().forEach(des ->{
            System.out.println(des); // 2,3,4,5,5
        });

    }

基礎となるオペレーティングSystem.arraycopyのメソッドが呼び出され、先端削除は、削除要素に後続のすべての要素がインデックス位置にコピー要素を除去するために、次いで、アレイの端部は、ヌル・ノードに設定されています

IIIまとめたもので

  1. コレクションは、追加、削除、設定、反復プロセスの次の、前の操作を発生するメカニズムを、高速フェイルトリガーされます、変更はConcurrentModificationExceptionをスローしますことが期待されていません。
  2. ArrayListのと同様の検索が駆動され、遅い付加および欠失のアレイの実装、すべての長所と短所の根底にある動的オブジェクト・アレイのArrayList。
  3. ArrayListの初期容量は膨張要素10を追加すること、0であり;より長さ当たり10プラスアレイの元の長さの50%の拡張後、Integer.MAX_VALUEでのArrayListの最大長 - 8。ための所定の指定されたサイズの容量初期のArrayList ArrayListには、指定されたサイズを返します。
  4. 再割り当ては、アレイ位置の値を設定することで、更新操作に対応し、所定の位置には要素が存在しない場合、例外がスローされます。
  5. 要素インデックスの場所を除去するために、基礎となるオペレーティングSystem.arraycopyのメソッドが呼び出され、削除、コピー要素へのすべての後続の要素を削除し、そのノードは、アレイヌルの端部に設定されています。

半日が経過し、知識を求める人は、それArrayListのソース他の方法を修正する時間を持っています。

おすすめ

転載: www.cnblogs.com/zszxz/p/12633169.html