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

java集合工具类Collections中的“坑”

2016-12-19 17:19 127 查看
java集合工具类Collections包括以下几个方法:

public static final <T> List<T> emptyList()


public static final <T> Set<T> emptySet()


public static final <K,V> Map<K,V> emptyMap()


在实际使用时发现通过以上方法创建的集合无法进行添加或删除操作。为什么返回的都是List/Set/Map,原生List/Set/Map和工具类创建的实例就不同呢?通过进入java源码查看后发现两者的确有很大的不同。

1.查找原因

首先通过方法定义就可以看到这些方法都是final的,因此他们是不可变(immutable)的。这就解释了为什么创建的集合无法进行
add()
等操作。java API的解释:Returns an empty list (immutable).即返回一个不可变的空list。

其次,既然是不可变的,那么
Collections.emptyList()
new ArrayList()
有什么区别呢?通过查阅Collections源码可以发现两者其实有很大区别。

emptyList()
的源码如下:

public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}


emptyList()
方法调用了EMPTY_LIST常量,EMPTY_LIST的定义如下:

@SuppressWarnings("rawtypes")
public static final List EMPTY_LIST = new EmptyList<>();


EMPTY_LIST常量定义的时候就初始化为
EmptyList<>()
,那么
EmptyList<>()
又做了哪些事?其源码如下:

private static class EmptyList<E>
extends AbstractList<E>
implements RandomAccess, Serializable {
private static final long serialV
4000
ersionUID = 8842843931221139166L;

public Iterator<E> iterator() {
return emptyIterator();
}
public ListIterator<E> listIterator() {
return emptyListIterator();
}

public int size() {return 0;}
public boolean isEmpty() {return true;}

public boolean contains(Object obj) {return false;}
public boolean containsAll(Collection<?> c) { return c.isEmpty(); }

public Object[] toArray() { return new Object[0]; }

public <T> T[] toArray(T[] a) {
if (a.length > 0)
a[0] = null;
return a;
}

public E get(int index) {
throw new IndexOutOfBoundsException("Index: "+index);
}

public boolean equals(Object o) {
return (o instanceof List) && ((List<?>)o).isEmpty();
}

public int hashCode() { return 1; }

@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
return false;
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
}
@Override
public void sort(Comparator<? super E> c) {
}

// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
}

@Override
public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }

// Preserves singleton property
private Object readResolve() {
return EMPTY_LIST;
}
}


(以上源码版本为:jdk1.8.0_66)

一目了然,EmptyList是一个继承了AbstractList的静态内部类,但是它并没有覆写AbstractList类的
add()
以及
get()
等方法。同时
EMPTY_LIST
是final修饰的,这就注定了EmptyList也不能有
add()
等操作。

至此,也就明白了
Collections.emptyList()
new ArrayList<T>()
生成的
list
是不同的。那么问题来了,既然已经有
List
等类,为什么工具类
Collections
中还要再定义一个内部类去实现空集合?难道不是多此一举吗?当然不是。那么在使用时两者之间如何选择呢?

2.Collections.emptyList() VS new ArrayList()

2.1相同点

他们都是
AbstractList
的子类,都可以序列化(Serializable)。

2.2区别

Collections.emptyList()
返回一个不可变list,而
new ArrayList<T>()
返回的list是可变的。如果确实需要返回一个不可变的空list最好使用
Collections.emptyList()


Collections.emptyList()
多次调用只返回同一个list实例,而
new ArrayList<T>()
每次调用都会返回新的实例。如果程序中需要多次创建不可变的空list那么最好通过
Collections.emptyList()
来实现,因为它占用的资源比
new ArrayList<T>()
要少;

另外
ArrayList
的带参构造函数
ArrayList(int initialCapacity)
传入参数0,即返回
new ArrayList<>(0)
,以此来代替
new ArrayList<>()
也可以创建一个不可变的集合对象。因为
new ArrayList<>(0)
返回的是一个不可变的空数组。如下源码所示:

public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;//EMPTY_ELEMENTDATA为一个不可变空数组
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}


EMPTY_ELEMENTDATA源码如下所示:

private static final Object[] EMPTY_ELEMENTDATA = {};


(以上源码版本为:jdk1.8.0_66)

同样在
emptySet()
emptyMap()
等类似的方法也会有类似的现象,就不再一一列举。

3.总结

当我们带着问题深入源码的时候总能发现意想不到的事。既然JDK有
Collections.emptyList()
这样的实现,必定有其特殊的用意。因此我们要时常去思考问题的本质,去打破砂锅问到底这样才能弄清楚问题的真相。

如有错误请批评指出!

参考文献

1.Collections.emptyList

2.Collections.emptyList() vs. new instance
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: