国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java集合干貨——ArrayList源碼分析

wupengyu / 3122人閱讀

摘要:關(guān)于的具體實現(xiàn),一些基本的都也知道,譬如數(shù)組實現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如初始化的長度,擴(kuò)容等。

前言

在之前的文章中我們提到過ArrayList,ArrayList可以說是每一個學(xué)java的人使用最多最熟練的集合了,但是知其然不知其所以然。關(guān)于ArrayList的具體實現(xiàn),一些基本的都也知道,譬如數(shù)組實現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如:初始化的長度,擴(kuò)容等。

本篇主要通過一些對源碼的分析,講解幾個ArrayList常見的方法,以及和Vector的區(qū)別。

ArrayList 定義
public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable

ArrayList實際上是一個動態(tài)數(shù)組,容量可以動態(tài)的增長,其繼承了AbstractList,實現(xiàn)了List, RandomAccess, Cloneable, java.io.Serializable這些接口。

RandomAccess接口,標(biāo)記接口,表明List提供了隨機(jī)訪問功能,也就是通過下標(biāo)獲取元素對象的功能。

1.RandomAccess接口,標(biāo)記接口,表明List提供了隨機(jī)訪問功能,也就是通過下標(biāo)獲取元素對象的功能。之所以是標(biāo)記接口,是該類本來就具有某項能力,使用接口對其進(jìn)行標(biāo)簽化,便于其他的類對其進(jìn)行識別(instanceof)。
2.ArrayList數(shù)組實現(xiàn),本身就有通過下標(biāo)隨機(jī)訪問任意元素的功能。那么需要細(xì)節(jié)上注意的就是隨機(jī)下標(biāo)訪問和順序下標(biāo)訪問(LinkedList)的不同了。也就是為什么LinkedList最好不要使用循環(huán)遍歷,而是用迭代器遍歷的原因。
3.實現(xiàn)RandomAccess同時意味著一些算法可以通過類型判斷進(jìn)行一些針對性優(yōu)化,例子有Collections的shuffle方法,源代碼就不粘貼了,簡單說就是,如果實現(xiàn)RandomAccess接口就下標(biāo)遍歷,反之迭代器遍歷

實現(xiàn)了Cloneable, java.io.Serializable意味著可以被克隆和序列化。

初始化

在使用中我們經(jīng)常需要new出來各種泛型的ArrayList,那么在初始化過程是怎樣的呢?

如下一行代碼,創(chuàng)建一個ArrayList對象

List list = new ArrayList<>();

我們來看源碼,是如何初始化的,找到構(gòu)造方法

//無參構(gòu)造方法
public ArrayList() {
  super();
  this.elementData = EMPTY_ELEMENTDATA;
}

看到這些代碼的時候,我也是不解的,elementData和EMPTY_ELEMENTDATA是什么啊?但是很明顯EMPTY_ELEMENTDATA是一個常量,追本溯源我們?nèi)タ匆幌鲁蓡T屬性。

//如果是無參構(gòu)造方法創(chuàng)建對象的話,ArrayList的初始化長度為10,這是一個靜態(tài)常量
private static final int DEFAULT_CAPACITY = 10;

//在這里可以看到我們不解的EMPTY_ELEMENTDATA實際上是一個空的對象數(shù)組
    private static final Object[] EMPTY_ELEMENTDATA = {};

//保存ArrayList數(shù)據(jù)的對象數(shù)組緩沖區(qū) 空的ArrayList的elementData = EMPTY_ELEMENTDATA 這就是為什么說ArrayList底層是數(shù)組實現(xiàn)的了。elementData的初始容量為10,大小會根據(jù)ArrayList容量的增長而動態(tài)的增長。
    private transient Object[] elementData;
//集合的長度
    private int size;

執(zhí)行完構(gòu)造方法,如下圖

可以說ArrayList的作者真的是很貼心,連緩存都處理好了,多次new出來的新對象,都指向同一個引用。

添加方法add
add(E e)
 /**
     * Appends the specified element to the end of this list.
     */
//增加元素到集合的最后
public boolean add(E e) {
  ensureCapacityInternal(size + 1);  // Increments modCount!!
  //因為++運(yùn)算符的特點(diǎn) 先使用后運(yùn)算  這里實際上是
  //elementData[size] = e
  //size+1
  elementData[size++] = e;
  return true;
}

先不管ensureCapacityInternal的話,這個方法就是將一個元素增加到數(shù)組的size++位置上。

再說回ensureCapacityInternal,它是用來擴(kuò)容的,準(zhǔn)確說是用來進(jìn)行擴(kuò)容檢查的。下面我們來看一下整個擴(kuò)容的過程

//初始長度是10,size默認(rèn)值0,假定添加的是第一個元素,那么minCapacity=1 這是最小容量 如果小于這個容量就會報錯
//如果底層數(shù)組就是默認(rèn)數(shù)組,那么選一個大的值,就是10
private void ensureCapacityInternal(int minCapacity) {
        if (elementData == EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //調(diào)用另一個方法ensureExplicitCapacity
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
      //記錄修改的次數(shù)
        modCount++;

        // overflow-conscious code
      //如果傳過來的值大于初始長度 執(zhí)行g(shù)row方法(參數(shù)為傳過來的值)擴(kuò)容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
//真正的擴(kuò)容
 private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
   //新的容量是在原有的容量基礎(chǔ)上+50% 右移一位就是二分之一
        int newCapacity = oldCapacity + (oldCapacity >> 1);
   //如果新容量小于最小容量,按照最小容量進(jìn)行擴(kuò)容
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
   //這里是重點(diǎn) 調(diào)用工具類Arrays的copyOf擴(kuò)容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

//Arrays
public static  T[] copyOf(U[] original, int newLength, Class newType) {
  T[] copy = ((Object)newType == (Object)Object[].class)
    ? (T[]) new Object[newLength]
    : (T[]) Array.newInstance(newType.getComponentType(), newLength);
  System.arraycopy(original, 0, copy, 0,
                   Math.min(original.length, newLength));
  return copy;
}

add(int index, E element)

添加到指定的位置

public void add(int index, E element) {
  //判斷索引是否越界,如果越界就會拋出下標(biāo)越界異常
  rangeCheckForAdd(index);
//擴(kuò)容檢查
  ensureCapacityInternal(size + 1);  // Increments modCount!!
  //將指定下標(biāo)空出 具體作法就是index及其后的所有元素后移一位
  System.arraycopy(elementData, index, elementData, index + 1,size - index);
  //將要添加元素賦值到空出來的指定下標(biāo)處
  elementData[index] = element;
  //長度加1
  size++;
}
//判斷是否出現(xiàn)下標(biāo)是否越界
private void rangeCheckForAdd(int index) {
  //如果下標(biāo)超過了集合的尺寸 或者 小于0就是越界  
  if (index > size || index < 0)
    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
remove(int index)

ArrayList支持兩種刪除元素的方式

remove(int index) 按照下標(biāo)刪除 常用

remove(Object o) 按照元素刪除 會刪除和參數(shù)匹配的第一個元素

下面我們看一下ArrayList的實現(xiàn)

 /**
 移除list中指定位置的元素
     * Removes the element at the specified position in this list.
     所有后續(xù)元素左移 下標(biāo)減1
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *參數(shù)是要移除元素的下標(biāo)
     * @param index the index of the element to be removed
     返回值是被移除的元素
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
public E remove(int index) {
  //下標(biāo)越界檢查
  rangeCheck(index);
//修改次數(shù)統(tǒng)計
  modCount++;
  //獲取這個下標(biāo)上的值
  E oldValue = elementData(index);
//計算出需要移動的元素個數(shù) (因為需要將后續(xù)元素左移一位 此處計算出來的是后續(xù)元素的位數(shù))
  int numMoved = size - index - 1;
  //如果這個值大于0 說明后續(xù)有元素需要左移  是0說明被移除的對象就是最后一位元素
  if (numMoved > 0)
    //索引index只有的所有元素左移一位  覆蓋掉index位置上的元素
    System.arraycopy(elementData, index+1, elementData, index,numMoved);
 // 將最后一個元素賦值為null  這樣就可以被gc回收了
  elementData[--size] = null; // clear to let GC do its work
//返回index位置上的元素
  return oldValue;
}

//移除特定元素
public boolean remove(Object o) {
  //如果元素是null 遍歷數(shù)組移除第一個null
  if (o == null) {
    for (int index = 0; index < size; index++)
      if (elementData[index] == null) {
        //遍歷找到第一個null元素的下標(biāo) 調(diào)用下標(biāo)移除元素的方法
        fastRemove(index);
        return true;
      }
  } else {
    //找到元素對應(yīng)的下標(biāo) 調(diào)用下標(biāo)移除元素的方法
    for (int index = 0; index < size; index++)
      if (o.equals(elementData[index])) {
        fastRemove(index);
        return true;
      }
  }
  return false;
}

//按照下標(biāo)移除元素
private void fastRemove(int index) {
  modCount++;
  int numMoved = size - index - 1;
  if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
                     numMoved);
  elementData[--size] = null; // clear to let GC do its work
}
ArrayList總結(jié)

底層數(shù)組實現(xiàn),使用默認(rèn)構(gòu)造方法初始化出來的容量是10

擴(kuò)容的長度是在原長度基礎(chǔ)上加二分之一

實現(xiàn)了RandomAccess接口,底層又是數(shù)組,get讀取元素性能很好

線程不安全,所有的方法均不是同步方法也沒有加鎖,因此多線程下慎用

順序添加很方便

刪除和插入需要復(fù)制數(shù)組 性能很差(可以使用LinkindList)

為什么ArrayList的elementData是用transient修飾的?

transient修飾的屬性意味著不會被序列化,也就是說在序列化ArrayList的時候,不序列化elementData。

為什么要這么做呢?

elementData不總是滿的,每次都序列化,會浪費(fèi)時間和空間

重寫了writeObject 保證序列化的時候雖然不序列化全部 但是有的元素都序列化

所以說不是不序列化 而是不全部序列化。

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
        // Write out array length
       s.writeInt(elementData.length);
    // Write out all elements in the proper order.
for (int i=0; i
ArrayList和Vector的區(qū)別
標(biāo)準(zhǔn)答案

ArrayList是線程不安全的,Vector是線程安全的

擴(kuò)容時候ArrayList擴(kuò)0.5倍,Vector擴(kuò)1倍

那么問題來了

ArrayList有沒有辦法線程安全?

Collections工具類有一個synchronizedList方法

可以把list變?yōu)榫€程安全的集合,但是意義不大,因為可以使用Vector

Vector為什么是線程安全的?

老實講,拋開多線程 它倆區(qū)別沒多大,但是多線程下就不一樣了,那么Vector是如何實現(xiàn)線程安全的,我們來看幾個關(guān)鍵方法

public synchronized boolean add(E e) {
  modCount++;
  ensureCapacityHelper(elementCount + 1);
  elementData[elementCount++] = e;
  return true;
}

public synchronized E remove(int index) {
  modCount++;
  if (index >= elementCount)
    throw new ArrayIndexOutOfBoundsException(index);
  E oldValue = elementData(index);

  int numMoved = elementCount - index - 1;
  if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
                     numMoved);
  elementData[--elementCount] = null; // Let gc do its work

  return oldValue;
}

就代碼實現(xiàn)上來說,和ArrayList并沒有很多邏輯上的區(qū)別,但是在Vector的關(guān)鍵方法都使用了synchronized修飾。

我不能保證每一個地方都是對的,但是可以保證每一句話,每一行代碼都是經(jīng)過推敲和斟酌的。希望每一篇文章背后都是自己追求純粹技術(shù)人生的態(tài)度。

永遠(yuǎn)相信美好的事情即將發(fā)生。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68232.html

相關(guān)文章

  • Java集合干貨——ArrayList源碼分析

    摘要:關(guān)于的具體實現(xiàn),一些基本的都也知道,譬如數(shù)組實現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如初始化的長度,擴(kuò)容等。 前言 在之前的文章中我們提到過ArrayList,ArrayList可以說是每一個學(xué)java的人使用最多最熟練的集合了,但是知其然不知其所以然。關(guān)于ArrayList的具體實現(xiàn),一些基本的都也知道,譬如數(shù)組實現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如:...

    Render 評論0 收藏0
  • Java集合干貨——LinkedList源碼分析

    摘要:前言在上篇文章中我們對對了詳細(xì)的分析,今天我們來說一說。他們之間有什么區(qū)別呢最大的區(qū)別就是底層數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)不一樣,是數(shù)組實現(xiàn)的具體看上一篇文章,是鏈表實現(xiàn)的。至于其他的一些區(qū)別,可以說大部分都是由于本質(zhì)不同衍生出來的不同應(yīng)用。 前言 在上篇文章中我們對ArrayList對了詳細(xì)的分析,今天我們來說一說LinkedList。他們之間有什么區(qū)別呢?最大的區(qū)別就是底層數(shù)據(jù)結(jié)構(gòu)的實現(xiàn)不一樣,...

    jsdt 評論0 收藏0
  • net - 收藏集 - 掘金

    摘要:再者,現(xiàn)在互聯(lián)網(wǎng)的面試中上點(diǎn)的都會涉及一下或者的問題個高級多線程面試題及回答后端掘金在任何面試當(dāng)中多線程和并發(fā)方面的問題都是必不可少的一部分。假如源碼分析之掘金概念是中集合的一種實現(xiàn)。 攻破 JAVA NIO 技術(shù)壁壘 - 后端 - 掘金現(xiàn)在使用NIO的場景越來越多,很多網(wǎng)上的技術(shù)框架或多或少的使用NIO技術(shù),譬如Tomcat,Jetty。學(xué)習(xí)和掌握NIO技術(shù)已經(jīng)不是一個JAVA攻城獅...

    岳光 評論0 收藏0
  • Java集合源碼分析系列-(一)ArrayList源碼剖析

    摘要:需要注意的是,通過構(gòu)造函數(shù)定義初始量是動態(tài)數(shù)組的實際大小。帶容量的構(gòu)造函數(shù)新建一個容量為的數(shù)組默認(rèn)構(gòu)造函數(shù),默認(rèn)為空構(gòu)造一個包含指定元素的第一個構(gòu)造方法使用提供的來初始化數(shù)組的大小。 前言 今天介紹經(jīng)常使用的一個Java集合類——ArrayList(基于JDK1.8.0_121)。ArrayList在工作和日常面試中經(jīng)常被使用或者提到。總的來說,工作中使用ArrayList主要是因為動...

    Miyang 評論0 收藏0
  • java源碼

    摘要:集合源碼解析回歸基礎(chǔ),集合源碼解析系列,持續(xù)更新和源碼分析與是兩個常用的操作字符串的類。這里我們從源碼看下不同狀態(tài)都是怎么處理的。 Java 集合深入理解:ArrayList 回歸基礎(chǔ),Java 集合深入理解系列,持續(xù)更新~ JVM 源碼分析之 System.currentTimeMillis 及 nanoTime 原理詳解 JVM 源碼分析之 System.currentTimeMi...

    Freeman 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<