(二)一块儿学 Java Collections Framework 源码之 AbstractCollection

.html

.java

.数组

.数据结构

.框架

目录函数

(一)一块儿学 Java Collections Framework 源码之 概述测试

(二)一块儿学 Java Collections Framework 源码之 AbstractCollectionui

 

java.util.AbstractCollection 类提供了 java.util.Collection 接口的骨干实现,也是 Java 集合框架(JCF, Java Collections Framework)中列表(List/Set)族相对较为顶层的实现类,这一点你们经过上一篇博文的 图1 就能够看出来了。此类是抽象类,所以仅仅实现了 Collection 接口中一些最基本的方法。因为此类没有实现 List 接口,因此它的子类未必都是有序的,所以它能够做为 List(有序)实现类和 Set(无序)实现类的共同祖先类。spa

JCF 为容器的基本实现提供了多个 Abstract 类,所以掌握了这些类以后,咱们本身基于这些 Abstract 类再实现新的容器时只要实现那些必要的方法便可。指针

下面咱们对 AbstractCollection 类中一些常见的方法实现逐个进行分析。

1.iterator() 方法

public abstract Iterator<E> iterator();

 

 此方法返回一个迭代器,该迭代器用于遍历这个列表中的每个元素。因为 java.util.Iterator 也是一个接口,因此不一样的数据结构实现(也就是不一样的子类)能够根据本身的须要来定义不一样的迭代方式,因此此方法被定义为一个抽象方法,没有对它进行实现。

2.isEmpty() 方法

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

 

这个很好理解,用来判断当前集合是否是空的,不作过多的解释。

3.contains(Object o) 方法

 1 public boolean contains(Object o) {
 2     Iterator<E> it = iterator();
 3     if (o==null) {
 4         while (it.hasNext())
 5             if (it.next()==null)
 6                 return true;
 7     } else {
 8         while (it.hasNext())
 9             if (o.equals(it.next()))
10                 return true;
11     }
12     return false;
13 }

 

此方法用来判断当前集合中是否包含一个与传入的参数 o 相同的元素。

想要匹配某个元素就必需要遍历集合了,因而先得到迭代器,而后根据参数 o 是否为 null 分别进行不一样的处理。

这里须要注意的是,在参数 o 不为 null 时采用的是 equals 方法来对比的。若是咱们要使用特定的规则来判断这个对象是否存在于某个集合中,经过重写该对象的 equals() 方法便可实现。

4.toArray() 方法

 1 public Object[] toArray() {
 2     // Estimate size of array; be prepared to see more or fewer elements
 3     Object[] r = new Object[size()];
 4     Iterator<E> it = iterator();
 5     for (int i = 0; i < r.length; i++) {
 6         if (! it.hasNext()) // fewer elements than expected
 7             return Arrays.copyOf(r, i);
 8         r[i] = it.next();
 9     }
10     return it.hasNext() ? finishToArray(r, it) : r;
11 }
12 
13 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
14 
15 @SuppressWarnings("unchecked")
16 private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
17     int i = r.length;
18     while (it.hasNext()) {
19         int cap = r.length;
20         if (i == cap) {
21             int newCap = cap + (cap >> 1) + 1;
22             // overflow-conscious code
23             if (newCap - MAX_ARRAY_SIZE > 0)
24                 newCap = hugeCapacity(cap + 1);
25             r = Arrays.copyOf(r, newCap);
26         }
27         r[i++] = (T)it.next();
28     }
29     // trim if overallocated
30     return (i == r.length) ? r : Arrays.copyOf(r, i);
31 }
32 
33 private static int hugeCapacity(int minCapacity) {
34     if (minCapacity < 0) // overflow
35         throw new OutOfMemoryError
36             ("Required array size too large");
37     return (minCapacity > MAX_ARRAY_SIZE) ?
38         Integer.MAX_VALUE :
39         MAX_ARRAY_SIZE;
40 }

 

toArray() 方法调用了两个私有方法,虽然看起来比较长,但解读起来并不复杂。

这个方法用于将集合中全部元素转换为数组。

首先建立一个与集合长度相同的数组 r,并经过迭代器将集合中的每个元素按照迭代的顺序依次保存到数组 r 中。

但因为获取集合长度的方法 size() 是由子类来实现的,因此没法避免两种状况发生:

1) size() 返回的长度比真正集合中元素的数量多;

2) size() 返回的长度比真正集合中元素的数量少。

数组 r 的长度又是固定的,怎么办?

第一种状况,经过 java.util.Arrays.copyOf(Object[] original, int newLength) 方法根据真正的元素数量再建立一个新的数组,而后将 r 内现有的元素拷贝进去再返回便可。

第二种状况稍微麻烦一点,由于此时并不知道集合中还有多少个元素没有被迭代出来,所以是不能直接肯定新分配的数组的长度的。

这时候两个私有函数就派上用场了:finishToArray(T[] r, Iterator<?> it) 方法会继续迭代这个集合,在遍历的过程当中会经过 java.util.Arrays.copyOf(Object[] original, int newLength) 方法分配一个新的数组并赋给变量 r,长度是原先 r 数组的1.5倍左右(newCap = cap + (cap >> 1) + 1),而后继续将集合中后面迭代出来的元素按顺序保存到数组 r 中。直到 r 再次满了,那么再重复上面的步骤继续分配空间。

最后一行 return (i == r.length) ? r : Arrays.copyOf(r, i); 的意思是,若按照前面每次分配 1.5 倍新空间的增加方式,若最终迭代完毕后整好填满了数组 r,那么直接返回 r 就能够了;若新分配的空间并无彻底被用完集合就遍历结束了,那么再从新分配一个与真实元素数量相同长度的数组来保存 r 的数据,并返回给调用者。关于这一点,你们看一下 java.util.Arrays.copyOf(Object[] original, int newLength) 的源码就明白了。

私有函数 hugeCapacity(int minCapacity) 的做用是检查分配的元素数量有没有超过上限(MAX_ARRAY_SIZE)。

5.toArray(T[] a) 方法

 1 @SuppressWarnings("unchecked")
 2 public <T> T[] toArray(T[] a) {
 3     // Estimate size of array; be prepared to see more or fewer elements
 4     int size = size();
 5     T[] r = a.length >= size ? a :
 6               (T[])java.lang.reflect.Array
 7               .newInstance(a.getClass().getComponentType(), size);
 8     Iterator<E> it = iterator();
 9 
10     for (int i = 0; i < r.length; i++) {
11         if (! it.hasNext()) { // fewer elements than expected
12             if (a == r) {
13                 r[i] = null; // null-terminate
14             } else if (a.length < i) {
15                 return Arrays.copyOf(r, i);
16             } else {
17                 System.arraycopy(r, 0, a, 0, i);
18                 if (a.length > i) {
19                     a[i] = null;
20                 }
21             }
22             return a;
23         }
24         r[i] = (T)it.next();
25     }
26     // more elements than expected
27     return it.hasNext() ? finishToArray(r, it) : r;
28 }

 

与 toArray() 方法相同,也是用于将集合转换为数组。

这里须要注意的两点:

1) 若参数 a 的长度小于集合的长度,此函数会在内部从新分配数组用于返回值,因此调用者的入参不会被改变。因此建议调用者使用其返回值而不是入参做为后续处理的数据源。

2) 若参数 a 的长度大于集合的长度,则参数 a 比此集合多出来的部分会被赋为 null。

你们不妨运行下面的代码,经过修改 len 来控制数组 ary0 的长度进行测试,观察运行结果。

 1 public static void main(String[] args) {
 2     AbstractCollection<String> c = new ArrayList<String>();
 3     c.add("a");
 4     c.add("b");
 5     c.add("c");
 6     c.add("d");
 7     int len = 5;
 8     String [] ary0 = new String [len];
 9     ary0[4] = "eee";
10     String [] ary1 = c.toArray(ary0);
11     print(ary0);
12     System.out.println("ary1.length = " + ary1.length);
13     print(ary1);
14 }
15 
16 private static void print(String [] ary) {
17     for (int i = 0; i < ary.length; i++) {
18         System.out.println("ary[" + i + "]" + ary[i]);
19     }
20 }

6.remove(Object o) 方法

 1 public boolean remove(Object o) {
 2     Iterator<E> it = iterator();
 3     if (o==null) {
 4         while (it.hasNext()) {
 5             if (it.next()==null) {
 6                 it.remove();
 7                 return true;
 8             }
 9         }
10     } else {
11         while (it.hasNext()) {
12             if (o.equals(it.next())) {
13                 it.remove();
14                 return true;
15             }
16         }
17     }
18     return false;
19 }

 

此方法用于从集合中删除一个与参数 o 相同的元素。

这个方法的实现与 contains(Object o) 方法十分类似,只不过是多调用了一下迭代器中的 remove() 方法而已。

一样须要注意这里比较对象使用的是 equals() 方法,所以能够经过重写对象 equals() 方法的方式来自定义删除规则。

7.containsAll(Collection<?> c) 方法

1 public boolean containsAll(Collection<?> c) {
2     for (Object e : c)
3         if (!contains(e))
4             return false;
5     return true;
6 }

 

此方法用来测试当前集合是否包含集合 c 中的每个元素。

实现方式很简单,迭代集合 c,测试其每个元素是否被包含在当前的集合中,如有任何一个元素不包含在当前集合中,则返回 false。

8.addAll(Collection<? extends E> c) 方法

1 public boolean addAll(Collection<? extends E> c) {
2     boolean modified = false;
3     for (E e : c)
4         if (add(e))
5             modified = true;
6     return modified;
7 }

 

此方法将集合 c 中的每个元素依次添加到本集合中。

但请注意,只要集合 c 中的任何一个元素成功添加到了当前集合中,即便其它元素所有添加失败了,此方法也会返回 true。因此官方 API 文档对于返回值给出的解释是:若是此 collection 因为调用而发生更改,则返回 true。

9.removeAll(Collection<?> c) 方法

 1 public boolean removeAll(Collection<?> c) {
 2     Objects.requireNonNull(c);
 3     boolean modified = false;
 4     Iterator<?> it = iterator();
 5     while (it.hasNext()) {
 6         if (c.contains(it.next())) {
 7             it.remove();
 8             modified = true;
 9         }
10     }
11     return modified;
12 }

 

此方法用于从当前的集合中移除全部与 c 集合相同成员的成员。此方法的做用并非无条件清空本集合,若需如此作,要调用 clear() 方法。

实现很是简单,迭代遍历,只要包含相同的成员就 remove() 掉。

这里的 Objects.requireNonNull(c); 用于判断入参 c 是否为 null,若为 null 就跑出空指针异常。java.util.Objects 类是从 JDK 1.7 版本开始提供的。

10.retainAll(Collection<?> c) 方法

 1 public boolean retainAll(Collection<?> c) {
 2     Objects.requireNonNull(c);
 3     boolean modified = false;
 4     Iterator<E> it = iterator();
 5     while (it.hasNext()) {
 6         if (!c.contains(it.next())) {
 7             it.remove();
 8             modified = true;
 9         }
10     }
11     return modified;
12 }

 

此方法与 removeAll(Collection<?> c) 方法偏偏相反,仅保留当前集合中与集合 c 中相同的元素,也就是说将本集合中与集合 c 中的不一样的元素都删掉。

实现方式与 removeAll(Collection<?> c) 方法彻底相同,只有测试包含的条件是相反的。

11.clear() 方法

1 public void clear() {
2     Iterator<E> it = iterator();
3     while (it.hasNext()) {
4         it.next();
5         it.remove();
6     }
7 }

 

无条件清空当前集合。迭代删除每个元素便可。

相关文章
相关标签/搜索