ArrayList动态增长方式

来源:互联网 发布:网络刷手一天能挣多少 编辑:程序博客网 时间:2024/05/22 08:21

ArrayList动态增长方式

ensureCapacityInternal-> ensureExplicitCapacity->grow-> hugeCapacity

  1. ensureCapacityInternal:判断数组是否非空,若为空,比较DEFAULT_CAPACITY(ArrayList默认容量为10)与minCapacity设最大值为容量值
  2. ensureExplicitCapacity:modCount++,若minCapacity大与数组长度时调用grow扩容
  3. grow扩容原数组长度50%,与minCapacity比较取最大值,若最终取值大与MAX_ARRAY_SIZE,调用hugeCapacity最大容量函数扩容
    注:上两步的比较均采用相减与0比较的方式,针对最大值溢出Integer.MAX_VALUE为负的情况
  4. hugeCapacity判断容量值是否<0(若int值大与Integer.MAX_VALUE,则内存溢出),是则抛出OOM异常。设最终容量为(minCapacity >MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
    注:试图分配更大数组时可能导致OutOfMemoryError:被请求数组的size超出VM界限
  5. grow调用Arrays.copyOf()复制数组

总结:

 分配次数
1千需要分配 11次
1万一级需要分配17次
10万 需要分配23次
100万需要分配28次

 因扩容为自增0.5倍,为避免不必要的内存泄漏,应该考虑用ensureCapacity方法

 申请扩容(大与DEFAULT_CAPACITY)的四种结果:
1. 原容量的1.5倍
2. 用户自定义minCapacity值
3. MAX_ARRAY_SIZE(等于Integer.MAX_VALUE-8)
4. Integer.MAX_VALUE


补充相关小知识点:

1.前面grow方法中为什么要采用相减与0比较的方式

public static void main(String[] args) {    // Integer的取值范围-2147483648 ~ 2147483647 (-2^31 ~ 2^31-1)    int i = Integer.MAX_VALUE + 1;    int max = Integer.MAX_VALUE;    int min = Integer.MIN_VALUE;    System.out.println(i > max-8);// false    System.out.println(i - max > 0);// true    System.out.println(min == i);// true    System.out.println(min == i);// true    arraytest();}

2.Arrays.copyof(···)与System.arraycopy(···)方法比较

//Arrays.copyof方法public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {    T[] copy = ((Object)newType == (Object)Object[].class)//判断是否为Object类型        ? (T[]) new Object[newLength]//若为Object类型生成一个Object数组        : (T[]) Array.newInstance(newType.getComponentType(), newLength);        //否则生成一个newType数组类型的数组        //Class.getComponentType():返回表示数组组件类型的 Class。如果此类不表示数组类,则此方法返回 null。    System.arraycopy(original, 0, copy, 0,                     Math.min(original.length, newLength));    return copy;}//System.arraycopy方法public static native void arraycopy(Object src,  int  srcPos,                                       Object dest, int destPos,                                       int length);

总结:
System.arraycopy可以指定复制目标数组与起始复制位置。嗯~思考一下作用:可以实现向数组中部插入删除耶~

而Arrays.copyof只是返回一个新数组,只能复制原数组从0到newLength的值,内部调用System.arraycopy实现

3.关于MAX_ARRAY_SIZE

/** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

这个为什么要取一个Integer.MAX_VALUE - 8呢?

  • 原注释提到说这块区域是为了存放一些头数据
  • 其实也是为了避免一些机器内存溢出,-8 是为了减少出错的几率
  • 数组的长度太大了,会导致JVM内存溢出,所以当你的内存足够大时,数组的扩容接近内存的最大值时,数组不会直接扩满,还会留一些空间(8这一个字节),保证这时候JVM还能有些内存空间去做GC,清除其他不用的内存来缓冲

关于这个问题的答案,保留个人想法,欢迎大家指正错误

0 0
原创粉丝点击