前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >你注意ArrayList扩容原理了吗

你注意ArrayList扩容原理了吗

作者头像
Spark学习技巧
发布2018-12-24 13:55:51
1.4K0
发布2018-12-24 13:55:51
举报
文章被收录于专栏:Spark学习技巧Spark学习技巧

本文主要是从java 1.6-1.8说一下ArrayList的初始容量大小及扩容的思路,主要是底层是ArrayList在扩容的时候会整个复制导致性能底下,所以在大致知道数组容量大小的时候要给定一个合适的初始大小,最大化减小复制的次数。

1. java 1.6

代码语言:javascript
复制
public ArrayList() {
  this(10);
 }

带参数的构造函数,初始化了一个长度为初始容量的数组:

代码语言:javascript
复制
public ArrayList(int initialCapacity) {
  super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
  this.elementData = new Object[initialCapacity];
    }

(2)add方法:

代码语言:javascript
复制
public boolean add(E e) {
  ensureCapacity(size + 1);  // Increments modCount!!
  elementData[size++] = e;
  return true;
 }

(3)扩容方法:

代码语言:javascript
复制
public void ensureCapacity(int minCapacity) {
  modCount++;
  int oldCapacity = elementData.length;
  if (minCapacity > oldCapacity) {
      Object oldData[] = elementData;
      int newCapacity = (oldCapacity * 3)/2 + 1;
          if (newCapacity < minCapacity)
    newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
  }
    }

此方法里,一旦发现容量不足,会自动扩充容量,新的大小是:

代码语言:javascript
复制
int newCapacity = (oldCapacity * 3)/2 + 1

也就是原有容量的1.5倍+1。然后通过底层的复制方法将原有数据复制过来:

代码语言:javascript
复制
elementData = Arrays.copyOf(elementData, newCapacity);

2.JDK1.7

(1)默认的构造函数 初试化时的长度为10:

代码语言:javascript
复制
public ArrayList() {
  this(10);
 }

带参数的构造函数,初始化了一个长度为初始容量的数组:

代码语言:javascript
复制
public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

(2)add方法:

代码语言:javascript
复制
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
 }

(3)扩容方法:

代码语言:javascript
复制
private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

这是JDK1.7 ArrayList扩容的关键方法:

代码语言:javascript
复制
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

此方法里,一旦发现容量不足,会自动扩充容量,新的大小是:

代码语言:javascript
复制
int newCapacity = oldCapacity + (oldCapacity >> 1);

也就是原有容量加上自己除以2的值。然后通过底层的复制方法将原有数据复制过来:

代码语言:javascript
复制
elementData = Arrays.copyOf(elementData, newCapacity);

综述所述,JDK1.6和JDK1.7的ArrayList在扩容方面是不同的,JDK1.7通过位移的方式,效率更高些。

3. JDK1.8

在1.8 arraylist这个类中,扩容调用的是grow()方法,通过grow()方法中调用的Arrays.copyof()方法进行对原数组的复制,在通过调用System.arraycopy()方法进行复制,达到扩容的目的。

(1). 几个重要的初始化值

代码语言:javascript
复制
存储数组元素的缓冲区
// non-private to simplify nested class access
transient Object[] elementData; 
默认空数组元素
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
默认初始化容量
private static final int DEFAULT_CAPACITY = 10;
数组的大小
private int size;
记录被修改的次数
protected transient int modCount = 0;
数组的最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8

(2). 几个重要的方法

一个空的构造方法

代码语言:javascript
复制
默认数组的长度为10
public ArrayList() {
    this.elementData  = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

(3). add方法

代码语言:javascript
复制
public boolean add(E e) {
扩充长度,在原来的大小上面加1
// Increments modCount!!
    ensureCapacityInternal(size + 1);  
添加元素
    elementData[size++] = e;
    return true;
}

(4). 确认内部容量

代码语言:javascript
复制
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(
calculateCapacity(elementData, minCapacity));
}

(5). 计算容量

代码语言:javascript
复制
private static int calculateCapacity (Object[] elementData, int minCapacity) {
如果这个数组等于空,返回最大的容量,否则,还是原来的容量
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
    }

(6). 确认扩展容量

代码语言:javascript
复制
private void ensureExplicitCapacity(int minCapacity) {
修改次数加1
    modCount++;
如果容量不够,调用增加容量方法
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

(7). 扩展方法

代码语言:javascript
复制
private void grow(int minCapacity) {
    // overflow-conscious code
获取原来数组容量的长度
    int oldCapacity = elementData.length;
新增加的容量长度为原来容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
新容量比老容量小,那么新的容量就是老的容量
    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:
 
调用复制方法,在原来元素上增加容量,这就是传说中的可变集合。用新长度复制原数组。
    elementData = Arrays.copyOf(elementData, newCapacity);
}

(8). 数组工具类的复制方法

代码语言:javascript
复制
public static <T> T[] copyOf (T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

(9). 具体的复制方法

代码语言:javascript
复制
public static <T,U> T[] copyOf(U[] original, int newLength, 
Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
获得者数组对象
    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;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 浪尖聊大数据 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档