您的位置:首页 > 编程语言 > Java开发

基于Java8的ArrayList常用方法详解

2018-04-02 17:40 766 查看
在Java里面ArrayList算得上是一个常用的数据结构,同时也是一个线程不安全的数据结构。

构造函数

public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}


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);
}
}


public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}


一般常用的是第一种和第二种,第三种用的比较少。从代码能看出来,如果调用构造函数的时候,不传参数就使用默认的空对象内存(这有点不好形容,是分配了内存,但是没有写数据),传了参数就直接构造相应大小的数组,集合一类的直接转换成数组。构造函数没什么好讲的,就那样。

add方法

用过ArrayList的应该都用过add方法。add一共重载了两个方法。

public boolean add(E e) {
ensureCapacityInternal(size + 1);  //确实容器数量足够
elementData[size++] = e;
return true;
}


public void add(int index, E element) {
rangeCheckForAdd(index);//检查坐标是否合法,防止数组越界
ensureCapacityInternal(size + 1);  //
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}


add(E e)这个方法是默认从当前数据末尾插入数据。这里可以看出ArrayList和HashMap扩容策略的不同,ArrayList是先扩大了的数组再添加数据,而HashMap是先添加数据再扩大容器。之所以两者扩容策略不一样是因为HashMap在使用了数组3/4大小的时候就进行扩容,而ArrayList是要用完了整个数组才会扩容,并且ArrayList一次只增加一半的大小,这个比HashMap一次翻一倍扩大方式要相对节约一点点。附上ArrayList源码扩容策略的核心源码,HashMap请查看我之前的文章:浅谈Java8的HashMap的扩容策略

if (minCapacity - elementData.length > 0)
grow(minCapacity);


int newCapacity = oldCapacity + (oldCapacity >> 1);


add(int index,E e)和add(E e)的用法大同小异,多了一个数据移动过程。

remove方法

划重点!!!

remove也重载了两个方法。

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;
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;
}


看到这两个方法的区别了吧,一个是基于数组坐标移除,返回移除的值或者抛出异常,一个是基于对象本身的移除,返回的是true或者false。来一组很有意思的测试案例。

public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(2);
list.add(333);
list.add(444);
list.add(555);
list.remove(2);
}


是移除索引为2的对象444这个数据还是说移除索引为0的对象2,凭直觉告诉我答案是什么?

答案是:移除索引为2的对象444。

再来一组更有意思的测试。

byte x1 = 2;
list.remove(x1);


short x2 = 2;
list.remove(x2);


long x3 = 2;
list.remove(x3);


还是上面那个集合,分别执行这三组代码,结果是一样吗?答案是否定的,前两个会移出索引为2的对象444,第三个不会移除任何数据。基础的重要性在这两个问题体现的淋漓尽致。我承认这两个问题有一定的刁难成分在里面,但两个问题的答案全是干货,包含了Java5过后的自动装箱拆箱机制、基础数据类型的隐式转换。

基础数据类型隐式转换

byte a = 2;
short b = a;
int c =b;
long d = c;


Java5的自动装箱和拆箱机制

int a = 2;
Integer b = a;
Integer c = 2;
int d = c;


因为隐式转换的存在,前两个答案没有任何问题。我讲讲为什么第三种方式没有移除任何数据。首先long类型是不能自动转成int类型的,所以不能调用remove(int index)这个方法,那么就只能调用remove(Object o)这个方法,long的封装类型为Long,并且Long重写了equals方法。

源码如下

public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}


这个方法一直是返回的false,所以在remove(Object o)里面,不会执行任何的移除数据操作。

clear真的clear吗?

public void clear() {
modCount++;
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}


源码可以清楚的看见,只是把数组持有的对象充值为null。但这并不是数组为空的,来张图说明。



简单的说clear这个方法只是把过程二的引用给断开了,但过程一的引用还是存在的,当你ArrayList的数组扩充的很大的时候,clear方法并不能起到调用者想要的内存释放作用的。如果深究,这个只是断开了引用,要等到GC触发的时候才会真的回收内存的,当然这个就有点扯远了。

isEmpty、size、get、set

public boolean isEmpty() {
return size == 0;
}


public int size() {
return size;
}


public E get(int index) {
rangeCheck(index);
return elementData(index);
}


public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}


因为ArrayList是一个线程不安全的数据结构,所以这个两个方法上面并没有任何亮点,只有索引是否越界的检查。

ArrayList常用方法的讲解就到这里。

如有疑问,欢迎留言!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 数据结构