前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java基础系列(四十六):Set & AbstractSet

Java基础系列(四十六):Set & AbstractSet

作者头像
山禾说
发布2019-08-21 15:30:49
5020
发布2019-08-21 15:30:49
举报
文章被收录于专栏:Vi的技术博客Vi的技术博客

Set

Set继承了 Collection接口,它本身也是一个接口,代表一种不能拥有重复元素的容器类型,更确切的说,集合不包含一对元素 e1e2 ,使得 e1.equals(e2)

通过 Set的一些实现,我们可以发现, Set是基于 Map进行实现的,所以 Set取值时不保证数据和存入的时候顺序一致,并且不允许空值,不允许重复值。下面我们来看一下 Set都给我们提供了哪些方法。

方法

首先, Set提供一些关于本身属性的接口:

代码语言:javascript
复制
/**
 * 返回 set 中的元素个数
 * @return  set中元素个数
 */
int size();

/**
 * 如果set中不包含任何元素,返回true
 * @return  如果set中不包含任何元素,返回true
 */
boolean isEmpty();

当然,也提供了去该集合中查询元素是否存在的接口:

代码语言:javascript
复制
/**
 * 如果set包含指定的元素,则返回 true
 * @param o 指定的元素
 * @return  如果 set 包含指定的元素,则返回 true。
 */
boolean contains(Object o);

/**
 * 如果此 set 包含指定 collection 的所有元素,则返回 true。
 * 如果指定的 collection 也是一个 set,那么当该 collection 是此 set 的 子集 时返回 true。
 * @param c 检查是否包含在此 set 中的 collection
 * @return 如果此 set 包含指定 collection 中的所有元素,则返回 true
 */
boolean containsAll(Collection<?> c);

对于元素进行结构性操作的接口也有几个,这里需要注意的是,在添加元素的时候,如果该元素在集合中已经存在,会导致添加失败并返回一个false。

代码语言:javascript
复制
/**
 * 如果 set 中尚未存在指定的元素,则添加此元素
 * @param e 被添加的元素
 * @return  如果set中存在该元素,添加失败并返回false
 */
boolean add(E e);

/**
 * 如果 set 中没有指定 collection 中的所有元素,则将其添加到此 set 中
 * 如果指定的 collection 也是一个 set,则 addAll 操作会实际修改此 set,
 * 这样其值是两个 set 的一个 并集。如果操作正在进行的同时修改了指定的 collection,则此操作的行为是不确定的。
 * @param c
 * @return
 */
boolean addAll(Collection<? extends E> c);

/**
 * 如果 set 中存在指定的元素,则将其移除(可选操作)。
 * @param o 被删除的元素
 * @return  如果此 set 包含指定的对象,则返回true
 */
boolean remove(Object o);

/**
 * 仅保留 set 中那些包含在指定 collection 中的元素,换句话说,只取两者交集,其余的不管
 * @param c 与set进行判断的集合
 * @return  如果此 set 由于调用而发生更改,则返回 true
 */
boolean retainAll(Collection<?> c);


/**
 * 移除 set 中那些包含在指定 collection 中的元素,也就是说,取交集之外的所有元素
 * @param c 与set进行判断的集合
 * @return  如果此 set 由于调用而发生更改,则返回 true
 */
boolean removeAll(Collection<?> c);


/**
 * 移除此 set 中的所有元素,此调用返回后该 set 将是空的。
 */
void clear();

Set中提供了一个默认的获取可切割迭代器的一个实例,是通过 Spliterators方法进行获取

代码语言:javascript
复制
/**
 * 可切割的迭代器,返回的是该set集合的可切割迭代器的一个实例
 * @return
 */
@Override
default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, Spliterator.DISTINCT);
}

Set的方法到这里就告一段落了,可以看出其中的方法与 Collection相比并没有特别大的区别,下面我们来看看作为抽象实现类 AbstractSet中给我们提供了哪些基础的实现。

AbstractSet

通过源码我们可以看到, AbstractSet中提供了三个方法的重写,分别是 equalshashCoderemoveAll这三个方法,首先我们来看一下 equalshashCode是如何重写的。

代码语言:javascript
复制
/**
 * 比较指定对象与此 set 的相等性。如果给定对象也是一个 set,
 * 两个 set 的大小相等,并且给定 set 的每个成员都包含在此 set 中,则返回 true。
 * 这确保 equals 方法在 Set 接口的不同实现间正常工作。
 * @param o 被比较的元素
 * @return  如果相等返回true
 */
@Override
public boolean equals(Object o) {
    if (o == this) {
        return true;
    }
    if (!(o instanceof Set)) {
        return false;
    }
    Collection<?> c = (Collection<?>) o;
    if (c.size() != size()) {
        return false;
    }
    try {
        return containsAll(c);
    } catch (ClassCastException unused)   {
        return false;
    } catch (NullPointerException unused) {
        return false;
    }
}


/**
 * 返回此 set 的哈希码值。set 的哈希码被定义为该 set 中元素的哈希码的总和,其中 null 元素的哈希码被定义为 0。
 * 这确保了 s1.equals(s2) 意味着对于任何两个 set s1 和 s2,都有 s1.hashCode()==s2.hashCode()。
 * @return
 */
@Override
public int hashCode() {
    int h = 0;
    Iterator<E> i = iterator();
    while (i.hasNext()) {
        E obj = i.next();
        if (obj != null) {
            h += obj.hashCode();
        }
    }
    return h;
}

可以看出, equals方法保证了调用该方法的两个对象必须是实现了 Set接口的,而且具有一些的容错性,即 Set的不同子类之间也可以使用 equals方法来判断两个对象是否相等,而 hashCode方法的计算方式则是利用了迭代器,将每一项不为null的元素的哈希值相加而得到的,这样就可以保证了对于任意的两个对象,他们的哈希值都是相等的,这和 equals方法相匹配,符合了 Object中对于 equalshashCode方法的要求。

下面还有一个 removeAll方法,我们一起来看看

代码语言:javascript
复制
/**
 * 从此 set 中移除包含在指定 collection 中的所有元素
 * 如果指定 collection 也是一个 set,则此操作有效地修改此 set,从而其值成为两个 set 的 不对称差集。
 *
 * @param c 包含将从此 set 中移除的元素的 collection
 * @return 如果此 set 由于调用而发生更改,则返回 true
 */
@Override
public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    boolean modified = false;
    //通过在此 set 和指定 collection 上调用 size 方法,此实现可以确定哪一个更小。
    if (size() > c.size()) {
        // 如果此 set 中的元素更少,则该实现将在此 set 上进行迭代,依次检查迭代器返回的每个元素,查看它是否包含在指定的 collection 中。
        for (Iterator<?> i = c.iterator(); i.hasNext(); ) {
            //如果包含它,则使用迭代器的 remove 方法从此 set 中将其移除。
            modified |= remove(i.next());
        }
    } else {
        //如果指定 collection 中的元素更少,则该实现将在指定的 collection 上进行迭代,并使用此 set 的 remove 方法,从此 set 中移除迭代器返回的每个元素。
        for (Iterator<?> i = iterator(); i.hasNext(); ) {
            if (c.contains(i.next())) {
                i.remove();
                modified = true;
            }
        }
    }
    return modified;
}

可以看出,这个方法是使用了迭代器进行完成的,这里有些不太理解,为什么仅仅实现了这一个方法。或者说,为什么要在这里实现这个方法。希望知道的朋友可以告诉我~(微信号:cm_950825)。

原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Vi的技术博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Set
  • 方法
  • AbstractSet
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档