Collection容器家族(ArrayList源码详解)
一.ArrayList在Colection集合体系中的位置及概述
ArrayList理解起来很简单,Array数组 阵列的意思、List线性表 列表,加在一起就是数组去实现线性表的一个实现类。ArrayList是由AbstractList超类特化而来。在功能上实现了add, get, set, remove等与存储结构相关的方法,我们重点理解这些功能。
ArrayList继承自AbstractList超类,实现list接口外、RandomAccess 随机访问接口,、Cloneable克隆元素接口、java.io.Serializable序列化。(public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable)
二、父类AbstractList已经实现List接口,为什么ArrayList类还要去实现List接口?
ArrayList继承了AbstractList,实现了List接口。这里面存在着一个问题,就是AbstractList已经实现了List接口,那么我们ArrayList又继承了AbstractList为什么还要实现List接口呢?咱们根据下面的示例代码来看一下可能会存在的问题。
public class Day01Demo {
/**
* 交通工具接口
*/
private interface Moveable {
// 移动
public abstract void move();
// 输出信息
public abstract void show();
}
/**
* 交通工具抽象类
*/
private abstract class AbstractMoveable implements Moveable {
private String name = ""; // 交通工具名称
private String clas = ""; // 种类
private int custmerNum = 0; // 满载人数
public AbstractMoveable(String clas, int custmerNum, String name) {
this.clas = clas;
this.custmerNum = custmerNum;
this.name = name;
}
public abstract void move() ;
@Override
public void show() {
System.out.println("- 交通工具信息 - AbstractMoveable抽象类 -");
System.out.println("名称:" + this.name);
System.out.println("类型:" + this.clas);
System.out.println("满载:" + this.custmerNum);
}
@Override
public String toString() {
return "AbstractMoveable{" +
"name='" + name + '\'' +
", clas='" + clas + '\'' +
", custmerNum=" + custmerNum +
'}';
}
}
/**
* 陆地工具---继承---交通工具超类---实现接口
*/
private class LandMoveable extends AbstractMoveable implements Moveable {
private String brandModel = ""; // 发动机型号
public LandMoveable(String clas, int custmerNum, String name, String brandModel) {
super(clas, custmerNum, name);
this.brandModel = brandModel;
}
@Override
public void move() {
System.out.println("陆地工具的move方式 - LandMoveable");
System.out.println(this.brandModel + " 发动机 1E65F 单缸 二行程");
}
@Override
public void show() {
super.show();
System.out.println("交通工具信息 - FlyMoveable");
System.out.println("发动机型号:"+this.brandModel);
}
@Override
public String toString() {
return super.toString() + "LandMoveable{" +
"brandModel='" + brandModel + '\'' +
'}';
}
}
/**
* 飞行工具---继承---交通工具超类
*/
private class FlyMoveable extends AbstractMoveable {
private String propellerModel = ""; // 推进器型号
public FlyMoveable(String clas, int custmerNum, String name, String propellerModel) {
super(clas, custmerNum, name);
this.propellerModel = propellerModel;
}
@Override
public void move() {
System.out.println("飞行工具的move方式 - FlyMoveable");
System.out.println(this.propellerModel + " 推进器 向后喷气");
}
@Override
public void show() {
super.show();
System.out.println("交通工具信息 - FlyMoveable");
System.out.println("推进器型号:"+this.propellerModel);
}
@Override
public String toString() {
return super.toString() + "FlyMoveable{" +
"propellerModel='" + propellerModel + '\'' +
'}';
}
}
public <T> T createProxy(T obj) {
return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理交通工具要调用 ->" + method.getName() + "<-方法,被代理的对象是-> " + obj.getClass().getSimpleName());
return method.invoke(obj, args);
}
});
}
@Test
public void runDemo() {
Moveable landMoveable = new LandMoveable("陆地工具", 13,
"奔驰", "M270发动机");
Moveable flyMoveable = new FlyMoveable("飞行工具", 107,
"播音747", "涡轮喷气");
System.out.println("==========使用接口==========");
landMoveable.move();
landMoveable.show();
System.out.println("===========================");
flyMoveable.move();
flyMoveable.show();
System.out.println("===========================");
System.out.println();
System.out.println("==========使用代理==========");
Moveable lm = createProxy(landMoveable);
lm.move();
lm.show();
System.out.println("===========================");
Moveable fm = createProxy(flyMoveable);
fm.move();
fm.show();
System.out.println("===========================");
}
}
运行结果:
根据上面演示代码,交通工具接口Moveable作用是规定其实现类必须实现的几种方法,那么AbstractMoveable所起到的作用是什么?我们换个角度考虑一下如果没有AbstractMoveable超类,那么无论是飞行交通工具还是陆地交通工具不但要继承Moveable接口,这两个类还会有很多的重复代码,也就体现出抽象类存在的意义:提取出子类所共有的属性和行为特征。也就体现出AbstractMoveable超类减少代码重复,把交通工具最基本的实现都去实现,留下需要扩展的功能让子类去实现,AbstractMoveable起到了“模板类”和“基类”的作用。
最终总结如下:(为什么父类实现了的接口子类还要去实现?)
1.根据上面代码演示情况,在不使用接口的动态代理情况下,二者毫无区别,都可以运用接口去实现多态;但是如果涉及到使用接口的动态代理就必须显示的实现Moveable接口,否者程序将报错。
2.易理解性,这样的风格通俗易懂简明扼要。因为这种显示的实现的规则是有意向的,我们无需翻看它到底是什么。如果没有显示的写,可能我们还要去翻看父类去找这个子类到底是哪个接口下的实现类。它增加了继承结构的可跟踪性。
三、成员变量源码详解
/**
*默认初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 所有空的arrayList共享的实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 创建ArrayList对象,未规定大小,默认时使用的数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 数组的元素存储在数组缓冲区种。
* ArrayList的容量是这个数组缓冲区的长度。
* 当空的 ArrayList 的 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 时
* 则调用扩容函数grow 的基数就是 (DEFAULT_CAPACITY + 需要增加的容量) ,根据这个容量去调用grow
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* ArrayList的大小(它包含的元素数)
*/
private int size;
从成员变量可知ArrayList使用数组存储数据(Object[] elementData), 初始容量大小为。在Object[] elementData前面有个transient修饰符,它起到什么作用呢?
因为我们知道ArrayList继承了序列化接口 java.io.Serializable,那么首先要了解什么是序列化。在我的理解中,序列化就是将一个对象的状态(其实反应一个对象的状态就是对象的所有信息)变成一连串的字节(字符序列)其实到机器中就是 0 ,1的二进制串。当然有序列化就会有反序列化,反序列化也就是把这个字符序列转变成为一个对象。
那么什么时候会用到序列化?
1.想把内存中堆中的保存到数据库或者是一个文件当中
2.想用socket套接字在网络上传输对象
3.想通过RMI在不同虚拟机间传输对象
以上三种情况需要用到序列化接口。
回过头来transient是什么作用呢,使声明为transient的属性不被序列化。想象一下应用场景,就拿冷酷无情的钱相关的示例来说,提款机取钱,我的银行账户是一个对象,我不希望这么绝密的信息在网络中传输,或者被存到哪里,我在取钱的时候用一次用完就不用了。所以为了安全考虑,这些信息就会被加上transient关键字。也就是说,我取钱,我输入了用户名密码如果我的密码被声明为transient,那么这个密码只能在内存中,一旦放到网络中传输或者是存到文件里那么这个密码就会是空的。
四、成员方法源码详解
看一个类,首先要看成员方法,之后就是看构造方法。构造方法是一个类的入口,具有十分重要的作用。
构造方法:(ArrayList共有三个构造方法,一个无参构造两个带参构造)
/**
* 功能:ArrayList无参构造方法
* 实现:
* 1.使用默认空数组初始化数据(elementData)
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 功能:ArrayList有参构造方法
* 参数:集合的初始化容量
* 实现:
* 1.如果参数大于0,创建容量为参数大小的Object类型数组并赋给数据缓冲数组
* 2.如果参数等于0,将空的成员变量数据赋给数据缓冲数组
* 3.其他情况抛出异常
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* 功能:ArrayList有参构造
* 参数:其他集合
* 实现:
* 1.调用集合的toArray方法将参数集合转换成Object数组并赋给数据缓冲数组
* 2.如果数据缓冲数组elementData大小不等于0:
* 2.1如果elementData属于Object[]类型,构造方法结束
* 2.2如果elementData不属于Object[]类型,使用Arrays的copyOf方法创建Object[]类型数组并赋给本身
* 3.如果数据缓冲数组elementData大小不等于0,将默认的空数组赋给elementData
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
成员方法:
ArrayList方法相对来说还是较多的,有些方法之间实现方式相似,在这里取一些基础的典型的方法,增删改查,着重解析扩容机制,在加上subList和Iterator。其他的方法不一一赘述。
1.底层数组存储结构扩容机制(核心)
数组可能是每个人最先了解的数据结构,它有如下特点:
a.只能存储同一种数据类型。
b.一旦初始化初始化长度固定。
c.数组中的元素与元素之间的内存地址是连续的。
d.Object数组可以存储任意数据类型。(java语言中Object是所有类的根类)
ArrayList底层存储的数据是数组,所以ArrayList集合具有数组的特点,它同样一次只能存储一种类型数据,在创建集合时使用泛型约束。但是问题又来了,就像上文中所说,如果底层使用的是数组,和集合是怎样可以无限制大小的添加元素的?它是通过扩容的机制来实现,这也是ArrayList集合的核心。下面将通过源码讲解它的实现机制。
/**
* 功能:增加指定容量
* 参数:扩展后集合容量大小(=原始容量大小 + 扩展大小)
* 实现:
* 1.确定要扩展的最小容量:
* 1.1如果集合数据存储数组(elementData)不是默认空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),扩展后集合容量大小(minExpand)为0
* 1.2如果集合数据存储数组(elementData)是默认空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),扩展后集合容量大小(minExpand)为10(DEFAULT_CAPACITY)
* 2.将参数(minCapacity)与最小扩展容量(minExpand)比较:
* 2.1如果大于minExpand,进行ensureExplicitCapacity操作
* 2.2如果小于minExpand,不进行操作
* 注:只要集合不是空集合,就会进行ensureExplicitCapacity操作
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
/**
* 功能:检查扩展后集合容量大小是否合理(扩展后大小要大于原集合大小)
* 参数:扩展后集合容量大小(=原始容量大小 + 扩展大小)
* 实现:
* 1.超类AbstractList的修改次数计数器modCount+1
* 2.如果扩展后集合容量大小岛屿原集合大小,就进行扩容操作;否则不进行任何操作
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 功能:真正扩展数据存储集合的方法
* 参数:扩展后集合容量大小(=原始容量大小 + 扩展大小)
* 实现:
* 1.获取原始集合长度(oldCapacity), 计算新集合长度(newCapacity), 默认会将原始集合扩充为原来的1.5倍
* 2.比较计算后集合长度(newCapacity)与参数要求长度:
* 2.1如果大于,不进行操作
* 2.2如果小于,将参数要求大小作为新集合大小
* 3.新集合大小(newCapacity)与集合最大容量比较(MAX_ARRAY_SIZE默认为Integer最大值 - 8) ---这步也就是要求大小超出集合最大值是做的操作
* 只能分配Integer最大值容量大小的集合
* 3.1如果小于,进行下一步集合复制操作
* 3.2如果大于,调用hugeCapacity方法,获取Integer最大值作为集合的最大容量
* 4.使用Arrays的copyOf方法,指定要复制的数组和要分配的长度,并将复制好的数组重新赋值给集合数据存储数组elementData
* 注:Arrays的copyOf方法底层调用的是System的arraycopy方法,arraycopy方法是由C/C++实现,有JAVA去调用,至于为什么使用
* C/C++去实现,可能是由于C语言操作数据更快。
*/
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
2.set方法 修改固定位置的元素
/**
* 功能:修改固定位置的元素
* 实现:
* 1.先检查要修改的索引是否越界
* 2.获取原来该索引处的元素并返回,之后进行修改
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
3.get方法 获取固定位置的元素
/**
* 功能:获取固定位置的元素
* 实现:
* 1.检查索引是否越界,获取数组下标对应的元素并返回
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
4.remove相关的方法
/**
* 功能:根据索引下标删除元素
* 实现:
* 1.通过arraycopy将数组迁移一位
*/
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
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
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
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
}
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
5.获取两个结合的交集
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
6.batchRemove方法
private boolean batchRemove(Collection<?> c, boolean complement) { //complement等于false是取差集,也就是删除和c一样的元素
final Object[] elementData = this.elementData; //等于true是取交集 留下和c一样的元素
int r = 0, w = 0;
boolean modified = false;//等于false是没修改之前的状态
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement) //挑出不在集合C中的元素,从当前集合的第0个位置开始存放。
elementData[w++] = elementData[r];
} finally {
<span style="color:#FF0000;">//描述一下这个关键点,首先arrayList在单线程操作的时候,执行到这r一定等于size
//只有在多线程共同争抢一个资源的时候才会出现 r大于 或者 r小于 的情况。那么我们做一下测试用例</span>
if (r != size) {
//此时r会比
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) { //然后从w开始都置换成null并且持久化size的大小
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
7.add相关的方法 无论是哪个add方法,都要先判断是否需要扩容
public boolean add(E e) { //添加一个元素不指定位置 默认在容器的最后添加
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) { //添加一个元素指定位置
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
public boolean addAll(Collection<? extends E> c) { //添加一个集合的元素不指定位置 默认在最后
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c) { //添加一个集合的元素并指定位置
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
8.readObject和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(); //对象输出流 s 选择默认写入默认的对象类型
//将当前类的非静态和非瞬态字段写入此流。这只能被类的writeObject方法序列化。
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size); //规定对象输出流s写入对象的个数
//将一个32位int值写入此流
// Write out all elements in the proper order.
for (int i=0; i<size; i++) { //写入指定的对象到对象流
s.writeObject(elementData[i]); //该对象的类 静态 非静态等字段都写进去
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {//接收一个输入流
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject(); //从当前流中读取当前类的非静态和非瞬态字段。这只能被类的readObject方法反序列化
// Read in capacity
s.readInt(); // ignored //读出集合实际元素的数量
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) { //挨个插入到集合之中
a[i] = s.readObject();
}
}
}
9.Iterator迭代器
Iterator迭代器在我的上一篇博客(https://blog.csdn.net/IdealSpring/article/details/81835771)做了详细的讲解,这里只讲解ArrayList之类扩展的新功能forEachRemaining方法。
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) { //对于每个元素执行一个事件直到所有元素迭代完毕
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
使用示例:
@Test
public void test1() {
String[] str = {"大", "王", "叫", "我", "来", "巡", "山"};
List<String> list = Arrays.asList(str);
/**
* 迭代器的forEachRemaining方法和集合本身的forEach方法功能是相同的
*/
list.forEach(x -> System.out.println(x));
System.out.println("------------------");
list.iterator().forEachRemaining(x -> System.out.println(x));
}
10.ListIterator迭代器 没有新加功能,见上一篇博客https://blog.csdn.net/IdealSpring/article/details/81835771
11.subList方法和SubList类
//我们看一下 sublist 方法
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(<strong><span style="color:#FF0000;"> this</span></strong> , 0, fromIndex, toIndex); //返回一个sublist对象
}
//把当前集合对象的this指针传进了sublist的构造器中
private class SubList extends AbstractList<E> implements RandomAccess {
<strong><span style="color:#3333FF;">private final AbstractList<E> parent;</span></strong> //sublist所有方法都是根据这个引用做的
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
<strong><span style="color:#CC9933;">this.parent = parent; </span></strong> //把this对象给了父类引用,由于sublist操作都操作parent,它所有的操作都是对当前集合的操作
this.parentOffset = fromIndex; //所以sublist能改变当前集合的值
this.offset = offset + fromIndex; //虽然名义上是集合的一段视图 但是还是能改集合结构
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
12.spliterator并行遍历迭代器,见上一篇博客https://blog.csdn.net/IdealSpring/article/details/81835771
13.JDK1.8后新添加的方法
a.按条件删除(使用lambda表达式)
@Override
public boolean removeIf(Predicate<? super E> filter) { // //删除所有满足条件的元素 条件是一个拦截器 也就是lamanda表达式
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
使用方法演示:
@Test
public void test2() {
Integer[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9};
List<Integer> list = new ArrayList<>();
for (Integer i : arr) {
list.add(i);
}
System.out.println("过滤前:" + list);
list.removeIf(i -> i % 2 == 0); // 过去掉偶数
System.out.println("过滤后:" + list);
}
运行结果:
b.对集合的所有元素进行操作
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) { //对原来的集合中的每个元素进行指定的操作
Objects.requireNonNull(operator); //用操作的结果替换当前的元素
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
使用方法演示:
@Test
public void test3() {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
System.out.println("操作前:" + list);
list.replaceAll(x -> x * 10); // 过去掉偶数
System.out.println("操作后:" + list);
}
运行结果:
c.自定义比较器,对集合内元素进行排序
没有这个方法前要想对集合内元素进行排序,元素必须实现Comparable接口、或者构造时创建Comparator接口的匿名内部函数作为构造器,并且Collection集合家族只有TreeSet支持这样的自定义排序。从JDK1.8以后ArrayList可以使用sort函数进行排序,这是一个很不错的扩展。
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) { // 对集合中的元素进行排序
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
使用方法演示:
@Test
public void test4() {
List<Integer> list = new ArrayList<>(Arrays.asList(19, 22, 39, 44, 15, 66, 37, 88, 19));
System.out.println("排序前:" + list);
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
System.out.println("排序后:" + list);
}
运行结果:
五、总结
这样ArrayList怎么实现,方法含义,如何使用就讲解完了。ArrayList基于数组操作,在数值固定的情况下,查改删都还是蛮快捷的,在Java1.8中又加入了新特性,使我们更好更快,更简洁的去操作它的ArrayList。
对于这个基于数组实现的集合,增删改查操作都是对数组进行的,这个我们看源码是应该很容易理解。在1.8之后,新添加的几个方法都很使用,如果感兴趣,不放使用一下,越用越舒服。