Java 集合学习笔记:Collection

Posted 笑虾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 集合学习笔记:Collection相关的知识,希望对你有一定的参考价值。

Java 集合学习笔记:Collection

UML

简介

Collection 表示包含了一组元素对象,它定义了一系列用来折腾这些元素的方法。给徒子徒孙们立好了规矩。
通常不直接实现这个接口,而是实现它的两个徒弟 ListSet
除非你要定义的是一个包含重复元素的无序 collection 。

方法和说明

限定符和类型方法和说明
boolean add(E e)
确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
void clear()
移除此 collection 中的所有元素(可选操作)。
boolean contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
boolean equals(Object o)
比较此 collection 与指定对象是否相等。
int hashCode()
返回此 collection 的哈希码值。
boolean isEmpty()
如果此 collection 不包含元素,则返回 true。
Iterator<E> iterator()
返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
boolean removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
int size()
返回此 collection 中的元素数。
Object[] toArray()
返回包含此 collection 中所有元素的数组。
<T> T[] toArray(T[] a)
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。

JDK8 新增 default 方法

限定符和类型方法和说明
default booleanboolean removeIf(Predicate<? super E> filter)
删除此集合中满足给定谓词的所有元素。遍历中删除元素的求星。
default StreamStream<E> stream()
返回以此集合为源的顺序流。
default StreamStream<E> parallelStream()
返回以此集合为源的可能并行的流。
default Spliterator Spliterator<E> spliterator()
在此集合中的元素上创建可拆分器迭代器。

AbstractCollection

抽象类 AbstractCollection 实现了一部分 Collection

isEmpty

因为此时还不知道底层实现类会使用什么数据结构,所以这里的size()是抽象的,留给子类去实现。

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

contains(Object o)

  1. 取迭代器,然后遍历所有元素,逐个判断如果发现与给定元素相等的,就返回 true
  2. 给定元素如果为 null 使用 == 判断是否相等,否则使用 equals
    public boolean contains(Object o) 
        Iterator<E> it = iterator();
        if (o==null) 
            while (it.hasNext())
                if (it.next()==null)
                    return true;
         else 
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        
        return false;
    

toArray

toArray()

返回包含此 collection 中所有元素的数组。如果 collection 对其迭代器返回的元素顺序做出了某些保证,那么此方法必须以相同的顺序返回这些元素。
返回的数组将是“安全的”,因为此 collection 并不引用返回的数组。(换句话说,即使 collection 底层实现就是数组,此方法也必须分配一个新的数组)。因此,调用者可以随意修改返回的数组。
此方法充当了基于Array API基于collection API 之间的桥梁。
此实现返回一个数组,它包含此 collection 的迭代器返回的所有元素,这些元素的排列顺序与数组的连续元素存储顺序相同,都是从index 0 开始。返回数组的长度等于迭代器返回的元素数,即使此 collection 的大小发生更改也是如此,这种情况可能发生在 collection 允许在迭代期间进行并发修改的时候。size 方法只是作为一个优化提示被调用;即使迭代器返回不同的元素数,也会返回正确的结果。

此方法等效于:
List<E> list = new ArrayList<E>(size());
for (E e : this)
list.add(e);
return list.toArray();

  1. 以当前集合size创建数组 r。(后续会多退少补)
  2. 遍历数组 r ,每次从迭代器 it 中取一个元素,填充到数组 r 中。
    2.1. 如果 迭代器 < 数组 迭代器到头后,将数组 r 复制一份,返回新数组。
    2.2. 否则如果:数组 r 遍历完,迭代器也正好取完,则返回数组r
    2.3. 否则如果:数组 r 遍历完,迭代器还有元素,则调用 finishToArray 扩容数组 r 继续填充。
    public Object[] toArray() 
        // 暂时用集合 size 创建数组。多了少了后面会处理的。
        Object[] r = new Object[size()];
        // 获取迭代器
        Iterator<E> it = iterator();
        // 循环数组 r 的长度。每次从迭代器 it 中返回一个结果填进数组 r
        // 如果填充到一半,迭代器已经完了,数组还没填满。
        // 则直接将数组 r 内容复制到【新数组】并返回。
        for (int i = 0; i < r.length; i++) 
            if (! it.hasNext()) // 元素比预期的少(迭代器到头了,数组还没填满)
                return Arrays.copyOf(r, i);
            r[i] = it.next();
        
        // 如果数组 r 填满了,迭代器还有内容 ,则调用 finishToArray 继续处理。
        return it.hasNext() ? finishToArray(r, it) : r;
    
    
    private static <T> T[] finishToArray(T[] r, Iterator<?> it) 
    	// 取数组后的长度。(也是填充新元素的索引的位置)
        int i = r.length;
        // 迭代器继续遍历
        while (it.hasNext()) 
        	// 取数组 r 当前长度为:容积(下面有扩容操作,所以这里每次都要获取更新)
            int cap = r.length;
            // 判断数组中【元素个数 i】与【容积 cap】如果相等,说明没空间了,就扩容数组 r。
            // 第一次执行到这,肯定是相等的。但数组扩容后,下一次再到这里【元素个数 i】就小于【容积 cap】了。
            // 每次向数组填充数据【元素个数 i】就+1,一直循环到下一次 i == cap 再次触发扩容。
            if (i == cap) 
            	// 新容积 = 容积 + (容积 / 2 ) + 1
                int newCap = cap + (cap >> 1) + 1;
                // 对于溢出情况的处理
                if (newCap - MAX_ARRAY_SIZE > 0) newCap = hugeCapacity(cap + 1); 
                // 复制数组 r 内容,并扩容到原 r 的 1.5 倍。
                r = Arrays.copyOf(r, newCap);
            
            // 迭代器中取出元素,填充到数组 r 的索引 i
            r[i++] = (T)it.next();
        
        // 去掉自动扩容多出来的位置。即:复制数组 r 第 0 到 i 的元素到新数组并返回。
        return (i == r.length) ? r : Arrays.copyOf(r, i);
    
    // 容器尺寸过小,报错,过大就返回最大值。
    private static int hugeCapacity(int minCapacity) 
        if (minCapacity < 0)
        	throw new OutOfMemoryError("Required array size too large");
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    

toArray(T[] a)

可以看作是 toArray() 的升级版,用于指定返回数组的元素类。

  1. 返回数组的类型,与给定的数组 a 一样。
  2. 如果我们给的数组 a 尺寸够用,则直接向 a 中填充数据。
  3. 所以我们可以估计好数组大小,创建 a 时直接指定大小,能省去扩容的消耗。
    public <T> T[] toArray(T[] a) 
        // 取当前 collection 大小,作为参考值。
        int size = size();
        // 如果给定的数组 a 够,就直接用 a, 否则创建一个新数组 r(类型为 T)接收数据。
        T[] r = a.length >= size ? a :(T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
        Iterator<E> it = iterator();

        for (int i = 0; i < r.length; i++) 
        	// 如果迭代器已经到头了。
            if (! it.hasNext())  // fewer elements than expected
            	// 使用的就是给定数组 a ,将当前索引位置都设为 null
                if (a == r) 
                    r[i] = null; // null-terminate
                // 如果数组 a 长度小于已填充的元素个数 ,复制 r 到新数组并返回
                 else if (a.length < i) 
                    return Arrays.copyOf(r, i);
                // 如果给定的数组 a 够大,将 r 中数据复制到 a 并返回,末尾空位都填充 null
                 else 
                    System.arraycopy(r, 0, a, 0, i);
                    if (a.length > i) 
                        a[i] = null;
                    
                
                return a;
            
            // 从迭代器取出元素填进数组 r
            r[i] = (T)it.next();
        
        // 如果数组填满,还没放完,扩容数组,继续填充。同 toArray()
        return it.hasNext() ? finishToArray(r, it) : r;
    

add(E e)

实现了个寂寞。直接抛锅不支持的操作异常

	public boolean add(E e) 
        throw new UnsupportedOperationException();
	

remove(Object o)

  1. 获取迭代器,遍历所有元素。
  2. 目标元素 o 为 null 时使用 == 判断,否则使用 equals
  3. 删除操作,调用迭代器的 remove() 实现。
    public boolean remove(Object o) 
        Iterator<E> it = iterator();
        if (o==null) 
            while (it.hasNext()) 
                if (it.next()==null) 
                    it.remove();
                    return true;
                
            
         else 
            while (it.hasNext()) 
                if (o.equals(it.next())) 
                    it.remove();
                    return true;
                
            
        
        return false;
    

containsAll(Collection<?> c)

遍历给定集合 c 的每个元素,判断它是否包含在当前集合 this 中。
只要 c 中有一个元素,不被 this 包含,就返回 false
否则 c 中每个元素在当前集合中都存在,表示当前集合完全包含给定集合 c

    public boolean containsAll(Collection<?> c) 
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    

addAll(Collection<? extends E> c)

  1. 遍历给定集合 c 逐个向当前 this 集合中添加。
  2. 开头申明了一个 modified 标记编辑状态。只要 for 中的 add 操作成功一次,就更新为 true
    public boolean addAll(Collection<? extends E> c) 
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    

removeAll(Collection<?> c)

  1. 如果给定集合 cnull 抛锅。
  2. 申明一个 modified 标记编辑状态。只要 while 中的 remove 操作成功一次,就更新为 true
  3. 删除操作,底层通过当前集合的迭代器实现。
  4. 只要给定集合 c 包含迭代器 next() 返回的对象,则执行 remove 操作。
    public boolean removeAll(Collection<?> c) 
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) 
            if (c.contains(it.next())) 
                it.remove();
                modified = true;
            
        
        return modified;
    

retainAll(Collection<?> c)

removeAll 相反,c 不包含 next() 返回的对象就删除。(即:包含就保留)

    public boolean retainAll(Collection<?> c) 
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<E> it = iterator();
        while (it.hasNext()) 
            if (!c.contains(it.next())) 
                it.remove();
                modified = true;
            
        
        return modified;
    

clear()

获取迭代器,逐个删除。

    public void clear() 
        Iterator<E> it = iterator();
        while (it.hasNext()) 
            it.next();
            it.remove();
        
    

toString()

  1. 获取迭代器,判断如果没内容就返回字符串 []
  2. 创建 StringBuilder 将每个元素添加进去。直到所有元素添加完 sb.toString();
    public String toString() 
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) 
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        
    

参考资料

Collection 单列集合(单值)

以上是关于Java 集合学习笔记:Collection的主要内容,如果未能解决你的问题,请参考以下文章

Java 集合学习笔记:Collection

Java 集合学习笔记:Collection

java集合学习笔记

尚硅谷_Java零基础教程(集合Collection:list,set;map)-- 学习笔记

Java入门笔记之(工具包的学习1~集合与collection)

JAVA入门之Collection集合 笔记(35)