公众号merlinsea
- ArrayList
- ArrayList如果采用默认不带参数的构造函数,那么数组初始长度为0。线程不安全,只有第一次加入了元素以后才会把容量默认增加到10。
- 扩容的机制是:newlen = oldlen + oldlen/2
- ArrayList扩容机制代码解释:
- 第一步:按照正常情况进行扩容:newCapacity = oldCapacity + oldCapacity/2
- 第二步:如果正常扩容后的大小newCapacity比需要的最小容量minCapacity小,那么把newCapacity=minCapacity
- 第三步:判别newCapacity是否大于允许的最大值,如果是的话就设置newCapacity为最大值。
- grow(minCapacity) 表示我需要把数组扩容到最小为minCapacity的大小
- 首先按照正常扩容到方法 newLen = oldLen + oldLen/2
- 如果newLen >minCapacity 那么就扩容到newLen
- 如果newLen <= minCapacity ,那么就看看minCapacity会不会撑爆内存,如果不会就扩容到minCapacity,否则就扩容到最大值
- ArrayList的使用建议
- 当已经知道需要操作多少数量的元素时候,最好是指定容量,防止arraylist在后续添加的过程中自动扩容产生性能开销。
- ArrayList手写实现的核心点
- 有一个size用于记录实际上存储元素的长度大小
- 每次add()一个元素时,要保证size+1有空的位置来存放元素。
- 在保证size+1的位置时,ensureCapacity()函数,如果最小位置比数组长度大,说明需要扩容,扩容的时候首先按照newLen = oldLen + oldLen/2进行扩容,如果扩容后的newLen依旧不满足minCapacity的要求,就把newLen=minCapacity。然后判别newLen是否超出了最大长度。
public class MyArrayList implements Serializable { //使用这个字段,来判断当前集合类是否被并发修改,即迭代器并发修改的fail-fast机制 private transient int modCount = 0; //第一次扩容的容量 private static final int DEFAULT_CAPACITY = 10; //用于初始化空的list private static final Object[] EMPTY_ELEMENT_DATA = {}; //实际存储的元素,transient不能被序列化 transient Object[] elementData; //实际list集合大小,从0开始 private int size=0; public MyArrayList(){ this.elementData = EMPTY_ELEMENT_DATA; } public MyArrayList(int initialCapcity){ if(initialCapcity>0){ this.elementData = new Object[initialCapcity]; }else if(initialCapcity == 0){ this.elementData = EMPTY_ELEMENT_DATA; }else { throw new IllegalArgumentException("参数异常"); } } public boolean add(Object e){ //判断容量 ensureCapacityInternal(size+1); //使用下标赋值,尾部插入 elementData[size++] = e; return true; } //计算容量+确保容量 private void ensureCapacityInternal(int minCapacity){ //用于并发判断 modCount++; //如果是初次扩容,则使用默认的容量 if(elementData == EMPTY_ELEMENT_DATA){ minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //是否需要扩容,需要的最少容量大于现在数组的长度则要扩容 if(minCapacity - elementData.length > 0){ int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity>>1); //如果新容量 < 最小容量, 则讲最新的容量赋值给新的容量 if(newCapacity - minCapacity < 0){ newCapacity = minCapacity; } //创建新数组 Object[] objects = new Object[newCapacity]; //将旧的数组复制到新的数组里面 System.arraycopy(elementData,0, objects,0,elementData.length); //修改引用 elementData = objects; } } /** * 通过下标获取对象 * @param index * @return */ public Object get(int index){ rangeCheck(index); return elementData[index]; } private void rangeCheck(int index){ if(index > size || size < 0){ throw new IndexOutOfBoundsException("数组越界"); } } /** * 判断对象所在的位置 * @param o * @return */ public int indexOf(Object o){ if(o == null){ for(int i=0; i < size; i++){ if(elementData[i] == null){ return i; } } }else { for(int i=0; i<size; i++){ if(o.equals(elementData[i])){ return i; } } } return -1; } public Object set(int index, Object obj){ rangeCheck(index); Object oldValue = elementData[index]; elementData[index] = obj; return oldValue; } /** * 根据索引删除元素 * @param index * @return */ public Object remove(int index){ rangeCheck(index); //用于并发判断 modCount++; Object oldValue = elementData[index]; //计算要删除的位置后面有几个元素 int numMoved = size - index -1; if(numMoved>0){ System.arraycopy(elementData,index+1,elementData,index,numMoved); } //将多出的位置为空,没有引用对象,垃圾收集器可以回收,如果不为空,将会保存一个引用,可能会造成内存泄露 elementData[--size] = null; return oldValue; } //获取数组实际大小 public int size(){ return this.size; } }
算法训练营永久刷题班元旦优惠价,超低价格永久刷题。加入我们可以一起备战明年的春招笔试,快来参加吧~
算法永久刷题班元旦优惠价~~
奔跑的小梁,公众号:梁霖编程工具库
leetcode刷题直播教学,手把手带你刷题,元旦价格优惠通知,超低价格永久刷题