天天看点

Collection接口分析Colletion源码分析

文章目录

  • Colletion源码分析
    • 简介
    • API
    • 源码分析
    • 实战演练
      • 1:集合基本操作
      • 2:集合遍历
      • 3:toArray() 以及toArray()方法遇到的坑
      • 4:toArray(T[] a)
      • 5:contains()containsAll()
      • 6:retainAll(Collection<?> c)和removeAll(Collection<?> c);
      • 7:待补充

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区别