Collection接口

Collection接口

public interface Collection<E> extends Iterable<E> {}

它继承了Iterable接口,那么这个接口有什么用呢?

Iterable接口表示可迭代接口,它会返回一个迭代器Iterator。通过这个迭代器,我们可以遍历集合中元素了。

回到Collection接口,我们想一下,作为一个集合顶层接口,它应该包含哪些方法呢?想到那个对数据操作最经典的话,增删改查。

1、添加元素的方法

boolean add(E e);
boolean addAll(Collection<? extends E> c);

2、删除元素的方法

boolean remove(Object o);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear(); 

3、改这个操作在Collection接口没有,因为它没有提供索引,没办法更新对应索引的值。

4、查这个操作在Collection接口,集合中是否包含某些元素,以及返回迭代器遍历集合

boolean contains(Object o);
boolean containsAll(Collection<?> c);
Iterator<E> iterator(); 

5、其他重要方法

int size(); 
boolean isEmpty();
Object[] toArray();
<T> T[] toArray(T[] a);

抽象类AbstractCollection

AbstractCollection抽象类实现了Collection接口大部分方法,只剩下iterator()和size()方法需要子类复写,也就是说需要子类提供集合元素。

1、添加元素

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

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

add(E e) 方法直接抛出异常,表示AbstractCollection默认是不可修改的集合,子类必须复写这个方法,才能向集合中添加元素。

addAll(Collection<? extends E> c) ,通过for循环,将c集合中每个元素都添加到本集合中。

高级for循环这种格式,默认就是使用迭代器实现的,也就是说只要是Iterable 接口子类,都可以使用高级for循环。

2、删除元素

public boolean remove(Object o) {
    Iterator<E> it = iterator();
    // 注意这里,集合中有很多地方都是以o是否为空,分成两部分计算,虽然代码逻辑几乎一样。
    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;
}

删除单个元素,通过迭代器iterator,遍历集合中元素,如果与o相等,就通过迭代器的remove方法,将它删除。

注意这里会根据被删除元素o是否为空,分成两部分,虽然两部分代码逻辑几乎一模一样。如果设计成在循环中进行两个条件判断,这样也可以,但是会增加比较次数。

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;
}

通过迭代器遍历本集合中每个元素,如果c集合中也包含这个元素,那么就删除这个元素。

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;
}

这个方法和removeAll方法正好相反,遍历本集合中每个元素,如果c集合中不包含这个元素,那么就删除这个元素。

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

删除集合中全部元素。遍历集合中每个元素,得到一个删除一个,最终删除全部元素。

3、查找元素

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;
}

遍历每个元素,如果有和o对象相等的,就直接返回true。如果遍历全部元素,还是没有找到相等元素,就返回false。

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

是否包含c集合中全部元素。

4、重要方法

public abstract Iterator<E> iterator();
public abstract int size();

这两个方法等待子类复写。

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

通过size()方法判断集合是否为空。

public Object[] toArray() {
    // 将terator迭代器中的元素全部转换成Object[],我们必须知道集合元素的大小。
    // 但是size()返回值并不是集合元素的数量,所以要分情况
    Object[] r = new Object[size()];
    Iterator<E> it = iterator();
    for (int i = 0; i < r.length; i++) {
        // 迭代器已经遍历完成,说明集合元素数量就是i。用Arrays.copyOf(r, i)就可以返回i长度的Object数组了
        if (! it.hasNext())
            return Arrays.copyOf(r, i);
        r[i] = it.next();
    }
    // 如果it.hasNext()为true,表示集合中还有元素,那么就要扩大r数组,以便将剩余元素都存放进去。
    // 如果为false,那么表示集合数量就是size(),直接返回这个r数组。
    return it.hasNext() ? finishToArray(r, it) : r;
}

将集合转成Object[]数组,这个方法很重要。要转成数组,那么必须知道数组的大小。

这里需要注意的是size()返回值有可能和集合中元素数量不一样,因为强制约束它们两个值是相同的,要考虑到这种情况。 所以这里我们先创建一个size()长度的Object[],然后遍历迭代器,将值存入数组对应下标位置。然后就有三种情况:
  • 迭代器先遍历完成,就说明集合元素数量小于size()大小,而集合元素数量就是这个i, 然后通过Arrays.copyOf(r, i)方法,返回i长度的数组。
  • 迭代器和r数组一起遍历完成,那么集合元素长度就是size(),直接返回这个r数组。
  • 迭代器还没有遍历完成,那么就要对r数组进行扩容,存放剩余的集合元素,通过finishToArray(r, it)方法实现。
public <T> T[] toArray(T[] a) {
    // Estimate size of array; be prepared to see more or fewer elements
    int size = size();
    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
            if (a == r) {
                r[i] = null; // null-terminate
            } else if (a.length < i) {
                return Arrays.copyOf(r, i);
            } else {
                System.arraycopy(r, 0, a, 0, i);
                if (a.length > i) {
                    a[i] = null;
                }
            }
            return a;
        }
        r[i] = (T)it.next();
    }
    // more elements than expected
    return it.hasNext() ? finishToArray(r, it) : r;
}
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
    int i = r.length;
    while (it.hasNext()) {
        int cap = r.length;
        // 如果i == cap为true,表示数组已经满了,需要扩充。
        if (i == cap) {
            // 将数组长度扩充0.5倍。 (cap >> 1) 相当于 cap / 2
            int newCap = cap + (cap >> 1) + 1;
            // 防止数组长度超出Integer最大值
            if (newCap - MAX_ARRAY_SIZE > 0)
                newCap = hugeCapacity(cap + 1);
            // 将原数组元素新数组中,并返回新数组。
            r = Arrays.copyOf(r, newCap);
        }
        r[i++] = (T)it.next();
    }
    // trim if overallocated
    return (i == r.length) ? r : Arrays.copyOf(r, i);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError
            ("Required array size too large");
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

总结

Collection接口

Collection 接口继承自Iterable接口,Iterable接口表示一个可迭代接口,主要作用就是返回一个迭代器Iterator。

顶层迭代器Iterator有三个重要方法:

  • boolean hasNext();
  • E next();
  • void remove()。

用来遍历和移除集合中的元素。

Collection 接口中重要方法:

  • boolean add(E e); 向集合中添加一个元素
  • boolean addAll(Collection<? extends E> c); 将集合c中元素都添加到集合中。
  • boolean remove(Object o); 从集合中删除o元素,也有可能删除不成功
  • boolean removeAll(Collection<?> c); 从集合中删除集合c中包含的元素
  • boolean retainAll(Collection<?> c); 只保留c集合中包含的元素
  • void clear(); 清除集合中所有元素
  • boolean contains(Object o); 判断集合中是否包含元素o
  • boolean containsAll(Collection<?> c); 判断集合是否完整包含集合c中全部元素
  • Iterator<E> iterator(); 返回迭代器
  • int size(); 返回集合数量
  • boolean isEmpty(); 判断集合是否为空
  • Object[] toArray(); 将集合中元素转换成数组
  • <T> T[] toArray(T[] a); 将集合中元素转换成T类型的数组

抽象类AbstractCollection

  • 强制子类复写Iterator<E> iterator() 和int size() 方法
  • boolean add(E e) 方法直接抛出异常,如果不复写这个方法,那么集合是不能添加数据的。
  • boolean addAll(Collection<? extends E> c) 方法,是遍历集合,然后调用add方法,将元素添加到集合中,所以也会抛出异常。
  • remove(Object o) 方法,是调用迭代器的remove()实现的。
  • removeAll(Collection<?> c)方法,是通过迭代器遍历集合,然后使用集合c的contains方法,判断元素在集合中是否也存在,是的话就调用迭代器的remove()方法,删除这个元素。
  • retainAll(Collection<?> c)方法,与removeAll方法流程大体一样,只不过它是判断集合c中不包含就调用迭代器的remove()方法,删除这个元素。
  • void clear(); 通过迭代器遍历集合,然后调用r迭代器的remove()方法,删除每个元素
  • boolean contains(Object o); 通过迭代器遍历集合,查看是否有与o相等的元素。
  • boolean containsAll(Collection<?> c); 遍历集合c,拿集合c中每个元素调用本集合的contains方法。如果有返回false,那么containsAll方法就直接返回false。只有全部通过才返回true。
  • Object[] toArray(); 和<T> T[] toArray(T[] a); 通过迭代器将集合转化成对应数组。

 

上一篇:三目运算符空指针


下一篇:学习java基础知识判断字符串数组是否包含方法