浅看Android 中ArrayList源码
Posted 小陈乱敲代码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅看Android 中ArrayList源码相关的知识,希望对你有一定的参考价值。
重新翻了下Java看到泛型,描述的仍然是那么抽象。其实泛型这个东西没那么神秘,用的多了接触的多了,自然就理解了,但真要说个一二三也不一定能说的上来,真要说的话无非就是提一下泛型使用中的一些约束,或者定义泛型时的一些继承派生规则,通配符什么的。
总的来说,还是要去使用中理解泛型,Java和android中大量用到泛型,比如HashMap和ArrayList,当然还有很多也没办法都列举出来。正好想到这里就去看看ArrayList的源码吧。
一、ArrayList构造函数
泛型的好处,就是我们通过定义一个泛型类ArrayList,那么这个类只需要写一次就可以实现通用,比如我们可以
ArrayList<String> string1 = new ArrayList<String>();
这样就定义了一个字符串类型的ArrayList,同样我们可以传其他的对象进去,比如在Android中我们可以用它来装Activity,装Window都可以,这就是因为ArrayList是通过泛型的方式来定义的。我们在使用ArrayList的时候会先new出来一个对象,所以先看下源码中它的构造函数
/libcore/ojluni/src/main/java/java/util/ArrayList.java
164 public ArrayList(int initialCapacity)
//根据输入的int来决定数组的长度
165 if (initialCapacity > 0)
166 this.elementData = new Object[initialCapacity];
167 else if (initialCapacity == 0)
168 this.elementData = EMPTY_ELEMENTDATA;
169 else
170 throw new IllegalArgumentException("Illegal Capacity: "+
171 initialCapacity);
172
173
174
175 /**
176 * Constructs an empty list with an initial capacity of ten.
177 */
178 public ArrayList()
//无参构造器,直接给定一个默认数组长度
179 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
180
181
182 /**
183 * Constructs a list containing the elements of the specified
184 * collection, in the order they are returned by the collection's
185 * iterator.
186 *
187 * @param c the collection whose elements are to be placed into this list
188 * @throws NullPointerException if the specified collection is null
189 */
190 public ArrayList(Collection<? extends E> c)
//将传进来的集合变成数组
191 elementData = c.toArray();
192 if ((size = elementData.length) != 0)
193 // c.toArray might (incorrectly) not return Object[] (see 6260652)
194 if (elementData.getClass() != Object[].class)
195 elementData = Arrays.copyOf(elementData, size, Object[].class);
196 else
197 // replace with empty array.
198 this.elementData = EMPTY_ELEMENTDATA;
199
200
这里构造函数有三种,分成有参和无参两类,有参构造函数又分成ArrayList(int initialCapacity);和ArrayList(Collection<? extends E> c);构造函数比较简单,就是初始化一下数组的长度。当然这是一个泛型类
119 public class ArrayList<E> extends AbstractList<E>
120 implements List<E>, RandomAccess, Cloneable, java.io.Serializable
作为泛型类里面的普通方法,它的对象就是传入的泛型。数据结构产生的目的就是维护和管理数据,那么其本质就是增删改查,接下来看看ArrarList的增删改查是怎么做的
二、ArrayList增删改查
先来看下add是怎么做的,ArrayList中重构了两种add()函数
466 public boolean add(E e)
467 ensureCapacityInternal(size + 1); // Increments modCount!!
468 elementData[size++] = e;
469 return true;
470
471
481 public void add(int index, E element)
482 if (index > size || index < 0)
483 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
484
485 ensureCapacityInternal(size + 1); // Increments modCount!!
486 System.arraycopy(elementData, index, elementData, index + 1,
487 size - index);
488 elementData[index] = element;
489 size++;
490
这两个add,分别是向数组中添加一个对象,和向指定Index中添加一个对象,那么如果指定的这个Index中已经有元素了呢?通过看源码我们很容易发现,在Index已经存在元素的情况下去add,实际上是用当前值覆盖旧值的操作,那么也就相当于同时实现增和改的功能了。
看这两个函数结尾处,就是把要添加的元素放到指定下标的位置,然后给数组的size增加。在这之前需要注意一个函数ensureCapacityInternal(size + 1)
236 private void ensureCapacityInternal(int minCapacity)
237 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
238 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
239
240
241 ensureExplicitCapacity(minCapacity);
242
243
244 private void ensureExplicitCapacity(int minCapacity)
245 modCount++;
246
247 // overflow-conscious code
248 if (minCapacity - elementData.length > 0)
249 grow(minCapacity);
250
最终它会调用到grow(),我们在使用ArrayList时不用考虑它的长度,因为我们知道ArrayList是会自动扩容的,原理就在这里的grow()。
267 // overflow-conscious code
268 int oldCapacity = elementData.length;
269 int newCapacity = oldCapacity + (oldCapacity >> 1);
270 if (newCapacity - minCapacity < 0)
271 newCapacity = minCapacity;
272 if (newCapacity - MAX_ARRAY_SIZE > 0)
273 newCapacity = hugeCapacity(minCapacity);
274 // minCapacity is usually close to size, so this is a win:
275 elementData = Arrays.copyOf(elementData, newCapacity);
276
在grow()当中,也是一些基础的判断和异常筛查,当我们正常扩容走到这里的时候,这里新的数组容量newCapacity和旧容量oldCapacity的关系是什么呢?
int newCapacity = oldCapacity + (oldCapacity >> 1);
在二进制中,数字向右移动一位相当于除以2,所以可以说新数组的容量是旧数组容量的1.5倍。然后通过一个Arrays.copyOf()方法,将旧数组在添加到新数组当中。所以增的方法很简单,就是先判断是否需要扩容,如果需要的话就扩容到1.5倍然后将数据添加到对应下标的位置即可。需要注意到这里有个modCount
244 private void ensureExplicitCapacity(int minCapacity)
245 modCount++;
这里的modCount作用就相当于信号量,是用来提醒并发操作导致的异常,后面有很多地方会检查modCount,如果检查异常会报错
765 if (modCount != expectedModCount)
766 throw new ConcurrentModificationException();
767
看下remove操作
871 public void remove()
872 if (lastRet < 0)
873 throw new IllegalStateException();
874 if (modCount != expectedModCount)
875 throw new ConcurrentModificationException();
876
877 try
可以看到如果直接空参调remove的话是会移除最后一个元素
878 ArrayList.this.remove(lastRet);
879 cursor = lastRet;
880 lastRet = -1;
881 expectedModCount = modCount;
882 limit--;
883 catch (IndexOutOfBoundsException ex)
884 throw new ConcurrentModificationException();
885
886
1071 public E remove(int index)
//根据下标调用remove移除指定元素
1072 if (index < 0 || index >= this.size)
1073 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
1074 if (ArrayList.this.modCount != this.modCount)
1075 throw new ConcurrentModificationException();
//用的是父类的remove方法
1076 E result = parent.remove(parentOffset + index);
1077 this.modCount = parent.modCount;
1078 this.size--;
1079 return result;
1080
这里再往下看实际上remove也是调到了Cpp层的,在移除对应坐标元素之后,再返回新的数组。 数组的查询实际上也是很简单的
435 public E get(int index)
436 if (index >= size)
437 throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
438
439 return (E) elementData[index];
440
直接返回这个下标的对象就好了。这里的泛型E就是在初始化的时候数组中的对象类型。
在了解ArrayList的增删改查之后,就可以理解ArrayList中一个比较细节的地方,我们在初始化数组的时候,会用一个elementData来表示数组中的元素,这里是使用transient限定符来修饰的,这个限定符比较少见。
141 /**
142 * The array buffer into which the elements of the ArrayList are stored.
143 * The capacity of the ArrayList is the length of this array buffer. Any
144 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
145 * will be expanded to DEFAULT_CAPACITY when the first element is added.
146 */
147 // Android-note: Also accessed from java.util.Collections
148 transient Object[] elementData; // non-private to simplify nested class access
对于transient 修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略。 因此,transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。怎么去理解呢,实际上序列化就是把一个java对象转换成字节的形式,然后这个字节就可以被底层使用,用来储存或者是通信传输之类的,当然会占用一定的内存。那么基于对ArrayList源码的分析,首先ArrayList是一个不定长的数组,它的容量是不确定的,并且还会自己扩容,但是扩容之后并没有一个自动裁剪多余容量的机制,所以ArrayList中是可能出现数组很大,其中有用的元素却很少的情况。为了避免为过多的null值序列化造成的内存浪费,这里通过transient来修饰ArrayList中的元素变量,不参与Java的序列化,这样就避免无用功,可以节约内存。
当然ArrayList自己也有一套序列化机制,它还是可以保存起来也可以进行数据传输滴,不过那个就太底层了,我们从Android的角度不必去考虑,只是知道有这么回事就可以了。
转载: (https://juejin.cn/post/7170659798705242120),如有侵权,请联系删除。
以上是关于浅看Android 中ArrayList源码的主要内容,如果未能解决你的问题,请参考以下文章