文章目录
Colletion源码分析
简介
Collection 是最基本的集合接口,我们平常所用的Set和List都是实现了此接口。
API
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
default boolean removeIf(Predicate<? super E> filter) {}
default Spliterator<E> spliterator() {}
default Stream<E> stream(){}
default Stream<E> parallelStream(){}
源码分析
// 返回集合的数目
// 如果集合数目超过了整型的最大值,则返回Integer.MAX_VALUE
int size();
// 判断集合是否为空
boolean isEmpty();
// 判断集合是否包含某个对象
boolean contains(Object o);
// 返回集合的迭代器
Iterator<E> iterator();
// 将集合转成数组 并且顺序是要跟集合一致
Object[] toArray();
// TODO 和上面的作比较
<T> T[] toArray(T[] a);
// 添加元素
boolean add(E e);
// 删除元素
boolean remove(Object o);
// 判断集合是否包含指定集合的所有元素
boolean containsAll(Collection<?> c);
// 添加指定集合的所有元素到当前集合中
boolean addAll(Collection<? extends E> c);
// 1.8出来的默认接口,默认实现你无须管,因为实现此接口的类都会覆盖
// 里面传入一个lambda表达式
// 将会根据传入的条件删除元素,并返回TRUE或者FALSE
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
// 是否包含指定集合中的所有元素
boolean retainAll(Collection<?> c);
// 移除集合中的所有元素
void clear();
// 将此对象和集合相比较,注意不是和集合元素相比较
boolean equals(Object o);
// 返回集合的hashcode值
int hashCode();
// Iterable接口里的方法 ,并行遍历元素
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
// 1.8 新增的接口 将集合转换成流,然后操作
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
// 1.8 新增接口 返回集合的并行流
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
实战演练
1:集合基本操作
String[] str = {"a", "b", "c", "d", "e", "f"};
// 把数组转成List然后再赋值给Collection 多态
Collection c = Arrays.asList(str);
System.out.println("集合的大小:" + c.size() );
System.out.println("集合是否为空:" + c.isEmpty());
System.out.println("集合是否包含'a'元素:" + c.contains("a"));
思考一个问题
c.add(“g”)
和c.remove("g")
可以运行吗
答案是不可以的.
Exception in thread “main” java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at com.xl.ibrs.web.test.testC.main(testC.java:19)
可以看出在abstracList调用add方法的时候就报错了,那么到底是为什么呢。首先我们知道多态是在运行期间决定了引用变量指向的实例对象,也就是Arrays.asList(str)返回的实例对象,所以我们首先进入Arrays.asList():
// Arrays 类
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
// 这里省略源码
}
我们发现Arrays里面也有ArrayList,是以内部类呈现的,但是没有add,remove方法,所以会调用AbstractList的Add方法,我们进入到源码AbstractList方法去看:
public boolean add(E e) {
add(size(), e);
return true;
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
很明显的发现这是个可选方法,抛出了UnsupportedOperationException
异常,所以我们再使用Arrays.asList()方法进行添加删除的时候是会报错的,一定要注意哦。
2:集合遍历
// 拿到迭代器
Iterator<String> itr = c.iterator();
// 判断是否有下一个元素
while (itr.hasNext()){
System.out.println(itr.next());
}
3:toArray() 以及toArray()方法遇到的坑
toArray的源码(ArrayList中的实现):
public Object[] toArray() {
// 调用了数组的拷贝方法
return Arrays.copyOf(elementData, size);
}
toArray()的使用方式:
方式一:
String[] str = {"a", "b", "c", "d", "e", "f"};
Collection c = Arrays.asList(str);
String[] obj = (String[])c.toArray();
System.out.println(objs[0]);
方式二:Error
Collection<String> arr = new ArrayList<>(4);
arr.add("a");
String[] obj2 = (String[]) arr.toArray();
System.out.println(obj[0]);
猛然的乍一看是不是感觉要么这两个都对,要么这两个方式都错,然而并不是,第一种方式没有错,可以正常输出元素,但是第二种不可以,会报错:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
我门进入到Arrays.asList源码中,去搜索toArray():
// Arrays类中
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
// Arrays类中
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
@Override
public Object[] toArray() {
// 返回传入对象的浅拷贝
return a.clone();
}
}
而在ArrayList里面的toArray方法:
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
很明显可以看到,第一种方式将集合转成数组,其实是调用了Arrays里面的一个内部类ArrayList,重写了toArray()方法,返回的是对象的克隆,其实就是拷贝了一份引用而已,也就是说传入的是String[],然后经过toArray()方法返回Object[],而这个方法中实际的只是拷贝一份引用,而这个引用指向的类型是String[],但是返回的是Object[],即在这个过程中发生了元素的向上转型,之后我们获取元素再转成String[]类型又是元素的向下转型,是可以的,而第二种方式是拷贝数组返回的是Object[]类型数组,然后我们强转成String[],其实只发生了元素的向下转型,但是这个向下转型是Object->String,也就是说将父类强转成子类,这肯定是错的,第一种方式是String->Object->String,所以并不是网上很多说的不能将数组类型强转,只能强转单个元素类型
举个栗子看看:
String[] s1 = {"a"};
Object[] o = (Object[])s1;
String[] s2 = (String[])o;
System.out.println(s2[0]); // 输出a
4:toArray(T[] a)
toArray(T[])的源码(ArrayList中的实现):
public <T> T[] toArray(T[] a) {
// 如果传入的数组长度小于此集合的长度
if (a.length < size)
// 拷贝数组,返回一个新的数组实例
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// 调用System类的数组拷贝方法
System.arraycopy(elementData, 0, a, 0, size);
// 如果传入的数组长度大于集合的长度
if (a.length > size)
// 数组的最后一个元素为NULL
a[size] = null;
return a;
}
Arrays类中拷贝数组的方法:
// 拷贝数组的方法
@Param original 集合内部维护的数组
@Param newLengh 集合的长度
@Param newType T[]的class类型对象(运行时类)
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
System中的数组拷贝方法:
@Param src 集合内部维护的数组
@Param srcPos 集合内部维护的数组的开始下标
@Param dest 目标数组(传入的数组)
@Param destPos 目标数组的的开始下标
@Param length 集合内部维护的数组长度
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
由源码可知如果传入的数组长度小于集合长度,那么返回的就是集合长度的数组,如果传入的数组长度大于集合长度,那么多余的长度的元素都为null。
a.length > size
Collection<Integer> c = new ArrayList<>(16);
for (int i = 0; i < 4; i++) {
c.add(i);
}
Integer[] b = new Integer[6];
Integer[] a = c.toArray(b);
for (int i = 0; i < a.length; i++) {
// 输出 0 1 2 3 4 null null
System.out.println(a[i]);
}
a.lengh < size
Collection<Integer> c = new ArrayList<>(16);
for (int i = 0; i < 4; i++) {
c.add(i);
}
Integer[] b = new Integer[3];
Integer[] a = c.toArray(b);
for (int i = 0; i < a.length; i++) {
// 输出 0 1 2 3
System.out.println(a[i]);
}
toArray()和toArray(T[] a)方法总结
1. toArray()返回的类型是Object[],自己一定要注意是不是内部类的ArrayList,强转会出错,但不是不能用。
2. toArray(T[] a)返回的类型是传入的T[] 类型,不需要自己转换
3. 当传入的数组长度小于集合长度时候,将会重新创建一个数组实例
,长度为集合的长度,并且包括集合中的所有元素
4. 当传入的数组长度大于集合元素的长度时候,会调用System的复制数组方法,不会产生新的数组,长度为数组的长度,多余的空间将会为null
5:contains()containsAll()
- contains()
// 表示是否包含某个元素
String[] str = {"a", "b", "c", "d", "e", "f"};
Collection c = Arrays.asList(str);
// 判断是否包含某个元素
System.out.println( c.contains("a"));
- containsAll()
// 是否包含集合中的所有元素
ArrayList<Integer> a1 = new ArrayList<Integer>(16);
for (int i = 0; i < 6; i++) {
a1.add(i);
}
ArrayList<Integer> a2 = new ArrayList<Integer>(16);
for (int i = 0; i < 4; i++) {
a2.add(i);
}
ArrayList<Integer> a3 = new ArrayList<Integer>(16);
for (int i = 0; i < 8; i++) {
a3.add(i);
}
// true
System.out.println(a1.containsAll(a2));
// false
System.out.println(a1.containsAll(a3));
6:retainAll(Collection<?> c)和removeAll(Collection<?> c);
- retainAll(collection< ?> c)
从列表a中移除未包含在指定c集合中的所有元素
1:如果列表a中存在部分元素和c集合重复,那么移除之后a列表剩下的元素就是a和c的交集,返回true;
2:如果列表a中所有元素存在于c , 那么返回false,不做移除操作,a列表中元素不变
3:如果列表a和集合c 没有任何相同的元素,那么移除a 中所有元素,返回true
a列表和集合c存在部分重复元素
ArrayList<Integer> a1 = new ArrayList<Integer>(16);
/*0-5*/
for (int i = 0; i < 6; i++) {
a1.add(i);
}
/*0-3*/
ArrayList<Integer> a2 = new ArrayList<Integer>(16);
for (int i = 0; i < 4; i++) {
a2.add(i);
}
System.out.println(a1.retainAll(a2));// true
for (int i = 0; i < a1.size(); i++) {
// 输出了0-3
System.out.println(a1.get(i));
}
a列表完全存在于c 集合
ArrayList<Integer> a1 = new ArrayList<Integer>(16);
/*0-5*/
for (int i = 0; i < 6; i++) {
a1.add(i);
}
/*0-7*/
ArrayList<Integer> a3 = new ArrayList<Integer>(16);
for (int i = 0; i < 8; i++) {
a3.add(i);
}
System.out.println(a1.retainAll(a3)); false
for (int i = 0; i < a1.size(); i++) {
// 输出了0-5
System.out.println(a1.get(i));
}
a列表和集合c没有重复元素
ArrayList<Integer> a1 = new ArrayList<Integer>(16);
/*0-5*/
for (int i = 0; i < 6; i++) {
a1.add(i);
}
/*8-15*/
ArrayList<Integer> a4 = new ArrayList<Integer>(16);
for (int i = 8; i < 16; i++) {
a4.add(i);
}
System.out.println(a1.retainAll(a3)); true
for (int i = 0; i < a1.size(); i++) {
// 元素全部被删除
System.out.println(a1.get(i));
}
- removeAll()
从列表中删除collection中包含列表的全部元素(就是说,collection的元素存在于列表,都得删)
ArrayList<Integer> a1 = new ArrayList<Integer>(16);
/*0-5*/
for (int i = 0; i < 6; i++) {
a1.add(i);
}
/*0-3*/
ArrayList<Integer> a2 = new ArrayList<Integer>(16);
for (int i = 0; i < 4; i++) {
a2.add(i);
}
System.out.println(a1.removeAll(a2));
for (int i = 0; i < a1.size(); i++) {
// 输出了4和5 也就是说a1删除了0-3 ,都是存在于a2的元素
System.out.println(a1.get(i));
}
removeAll 和retainAll 总结:
retainAll 从列表 a中移除未包含在指定c集合中的所有元素
removeAll 从列表中删除collection中包含列表的全部元素
7:待补充
- Guava 的集合类,数组转集合 Lists.newArrayList(str)
- system.copyOf和Arrays.copyOf区别