数据结构 - Collection 接口

Collection接口.png
简介

Collection继承自Iterable,Collection接口是Java集合两大分支中的一支,Queue、List、Set都是Collection的扩展;集合大类分为了Collection和Map。

常见的数据结构:数组(Array)、集(Set)、队列(Queue)、链表(Linkedlist)、树(Tree)、堆(Heap)、栈(Stack)和映射(Map)等结构。

Iterable接口

实现这个接口的类允许使用 for-each loop 语句来遍历

public interface Iterable<T> {
    // 返回一个迭代器对象
    Iterator<T> iterator();
    // JDK1.8 新增遍历方式
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    // 可分割迭代器
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

Iterable最早出现在JDK 1.5,开始只有iterator()一个抽象方法,需要子类来实现一个内部迭代器Iterator遍历元素。

后两个方法是Java 8后新添加的,forEach(Consumer action)是为了方便遍历操作集合内的元素,Spliterator(splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比最早Java提供的顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历。

Collection 接口

Collection是一个高度封装的集合接口,它提供了所有集合要实现的默认方法

public interface Collection<E> extends Iterable<E>
基本方法
// 返回此集合的元素数量
int size();
// 返回集合是否为空
boolean isEmpty();
// 如果包含元素O则返回为true
boolean contains(Object o);
// 返回此集合元素的迭代器
Iterator<E> iterator();
// 将此集合转化为数组
Object[] toArray();
// 将此集合转化为制定类型数组
<T> T[] toArray(T[] a);
// 返回值是boolean,添加一个元素
boolean add(E e);
// 删除指定的元素
boolean remove(Object o);
// 如果包含集合C返回为true
boolean containsAll(Collection<?> c);
// 返回值是boolean类型,将集合C中的所有元素添加到此集合
boolean addAll(Collection<? extends E> c);
// 删除包含集合c的所有元素
boolean removeAll(Collection<?> c);
// 获取集合c与此集合的交集
boolean retainAll(Collection<?> c);
// 删除此集合中的所有元素
void clear();
// 将指定的对象O与此集合进行比较
boolean equals(Object o);
// 返回此集合的INT类型哈希码
int hashCode();
默认实现方法
// 删除满足条件的所有元素
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;
}
@Override
// 创建一个Spliterator
default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, 0);
}
// 返回以此集合作为源的顺序流
default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}
// 创建一个Spliterator并行流
default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}
AbstractCollection 接口

AbstractCollection 实现Collection接口,该类是一个抽象类,提供了对集合类操作的一些基本实现。List和Set的具体实现类基本上都直接或间接的继承了该类

public abstract class AbstractCollection<E> implements Collection<E>
构造函数
protected AbstractCollection() {
}
未实现的方法
    // 获取迭代器
    public abstract Iterator<E> iterator();
    // 获取集合元素个数
    public abstract int size();
已实现的方法

AbstractCollection所有已实现的方法子类都会覆盖掉,官方的说法 “此类提供集合的骨架实现接口,以最小化实现此接口所需的工作”

判空
public boolean isEmpty() {
    // 元素个数为0,就为空
    return size() == 0;
}
添加
public boolean add(E object) {
    throw new UnsupportedOperationException();
}

public boolean addAll(Collection<? extends E> c) {
    // 是否添加成功
    boolean modified = false;
    // 迭代参数中每一个元素
    for (E e : c)
        // 添加元素并判断是否添加成功
        if (add(e))
            modified = true;
    // 返回是否添加成功
    return modified;
}

直接调用AbstractCollection的add方法会抛出UnsupportedOperationException异常,所以我们在实现Collection自定义数据结构时一定要覆盖此方法。

删除
public boolean remove(Object object) {
    //获取子类实现的 迭代器
    Iterator<?> it = iterator();
    if (object != null) { // 参数不为空
        // 循环迭代
        while (it.hasNext()) {
            // 对比找到元素
            if (object.equals(it.next())) {
                // 移除
                it.remove();
                return true;
            }
        }
    } else { // 参数为空
        // 循环迭代,参数为空相当于清空
        while (it.hasNext()) {
            if (it.next() == null) {
                // 移除
                it.remove();
                return true;
            }
        }
    }
    return false;
}
​
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;
}
清空
public void clear() {
    // 获取子类实现的迭代器,挨个遍历,删除
    Iterator<E> it = iterator();
    // 循环迭代
    while (it.hasNext()) {
        it.next();
        // 单线程使用迭代器的 remove() 方法不会导致 fail-fast
        it.remove();
    }
}
包含
public boolean contains(Object o) {
    // 获取当前迭代器    
    Iterator<E> it = iterator();
    if (o==null) { // 参数为空
        // 迭代查找是否包含空元素
        while (it.hasNext())
            if (it.next()==null)
                // 包含空返回true
                return true;
    } else {
        // 迭代每一个元素进行比较
        while (it.hasNext())
            if (o.equals(it.next()))
                // 元素equals相等返回true
                return true;
    }
    return false;
}

public boolean containsAll(Collection<?> c) {
    // 遍历参数中每一个元素    
    for (Object e : c)
        // 有一个不包含就返回false
        if (!contains(e))
            return false;
    // 能到这儿说明都包含
    return true;
}
取交集
public boolean retainAll(Collection<?> collection) {
    boolean result = false;
    Iterator<?> it = iterator();
    // 迭代当前集合
    while (it.hasNext()) {
        // 参数集合中不包含,就在当前集合中移除此元素
        if (!collection.contains(it.next())) {
            // 移除
            it.remove();
            // 移除成功,结果置为true
            result = true;
        }
    }
    return result;
}
转数组
public <T> T[] toArray(T[] a) {
    // 获取集合长度
    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) {
                //如果数组是参数中的数组,则将剩余部分的值都设置为null
                r[i] = null; // null-terminate
            // 如果传入的数组长度小于集合长度
            } else if (a.length < i) {
                // 通过Arrays.copyOf将之前数组中的元素复制到新数组中
                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;
        // 起始位置
        if (i == cap) {
            // 新数组扩容,新长度 = 原长度*2 + 1
            int newCap = cap + (cap >> 1) + 1;
            // 新长度小于上限(int最大值-8)
            if (newCap - MAX_ARRAY_SIZE > 0)
                newCap = hugeCapacity(cap + 1);
            // 创建新数组并拷贝原数组
            r = Arrays.copyOf(r, newCap);
        }
        // 下标加1继续放值
        r[i++] = (T)it.next();
    }
    // 新数组
    return (i == r.length) ? r : Arrays.copyOf(r, i);
}
​
private static int hugeCapacity(int minCapacity) {
    // 长度小于0抛出越界异常
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError
            ("Required array size too large");
    // 长度超限默认最大值
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

对于toArray方法可能很多人会有疑问,toArray方法第二步如果数组长度不够不是已经建了新数组吗,为什么在迭代过程中还要对新数组进行扩容?

toArray不是线程安全的,在迭代的过程中,没有人能保证数据结构中元素不会增加或减少,减少到没关系,增加后新创建的数组放不下了怎么办?所以只能做扩容处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355