Java基础 集合(五)Set详解

本文详细介绍了Java集合框架中的Set接口、AbstractSet抽象类、SortedSet接口以及其重要子类如HashSet、LinkedHashSet和TreeSet的特性、实现和用途,着重讨论了它们的排序、线程安全性和使用场景。
摘要由CSDN通过智能技术生成

 

目录

 

简介

set种类

AbstractSet 抽象类

SortedSet 接口

HashSet

LinkedHashSet

TreeSet


前言-与正文无关

生活远不止眼前的苦劳与奔波,它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中,我们往往容易陷入工作的漩涡,忘记了停下脚步,感受周围的世界。让我们一起提醒自己,要适时放慢脚步,欣赏生活中的每一道风景,享受与家人朋友的温馨时光,发现那些平凡日子里隐藏的幸福时刻。因为,这些点点滴滴汇聚起来的,才是构成我们丰富多彩生活的本质。希望每个人都能在繁忙的生活中找到自己的快乐之源,不仅仅为了生存而工作,更为了更好的生活而生活。

送你张美图!希望你开心!

简介

       集合可以看作是一种容器,用来存储对象信息。所有集合类都位于java.util包下,值得一提的是支持多线程的集合类位于java.util.concurrent包下。

  Set接口定义了该类集合不允许存储重复的元素,且任何操作时均需要通过哈希函数映射到集合内部定位元素,集合内部的元素默认无序的。

 Set接口继承了Collection接口,是一个不包括重复元素的集合,更确切地说,Set 中任意两个元素不会出现 o1.equals(o2),而且 Set 至多只能存储一个 NULL 值元素,Set 集合的组成部分可以用下面这张图概括:

set种类

AbstractSet 抽象类

AbstractSet 是一个实现 Set 的一个抽象类,定义在这里可以将所有具体 Set 集合的相同行为在这里实现,避免子类包含大量的重复代码

所有的 Set 也应该要有相同的 hashCode() 和 equals() 方法,所以使用抽象类把该方法重写后,子类无需关心这两个方法。

public abstract class AbstractSet<E> implements Set<E> {
    // 判断两个 set 是否相等
    public boolean equals(Object o) {
        if (o == this) { // 集合本身
            return true;
        } else if (!(o instanceof Set)) { // 集合不是 set
            return false;
        } else {
            // 比较两个集合的元素是否全部相同
        }
    }
    // 计算所有元素的 hashcode 总和
    public int hashCode() { 
        int h = 0;
        Iterator i = this.iterator();
        while(i.hasNext()) {
            E obj = i.next();
            if (obj != null) {
                h += obj.hashCode();
            }
        }
        return h;
    }
}

SortedSet 接口

SortedSet 是一个接口,它在 Set 的基础上扩展了排序的行为,所以所有实现它的子类都会拥有排序功能。

public interface SortedSet<E> extends Set<E> {
    // 元素的比较器,决定元素的排列顺序
    Comparator<? super E> comparator(); 
    // 获取 [var1, var2] 之间的 set
    SortedSet<E> subSet(E var1, E var2); 
    // 获取以 var1 开头的 Set
    SortedSet<E> headSet(E var1); 
    // 获取以 var1 结尾的 Set
    SortedSet<E> tailSet(E var1); 
    // 获取首个元素
    E first(); 
    // 获取最后一个元素
    E last();
}

HashSet

HashSet 底层借助 HashMap 实现,我们可以观察它的多个构造方法,本质上都是 new 一个 HashMap

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
    public HashSet() {
        this.map = new HashMap();
    }
    public HashSet(int initialCapacity, float loadFactor) {
        this.map = new HashMap(initialCapacity, loadFactor);
    }
    public HashSet(int initialCapacity) {
        this.map = new HashMap(initialCapacity);
    }
}

我们可以观察 add() 方法和remove()方法是如何将 HashSet 的操作嫁接到 HashMap 的。

private static final Object PRESENT = new Object();

public boolean add(E e) {
    return this.map.put(e, PRESENT) == null;
}
public boolean remove(Object o) {
        return this.map.remove(o) == PRESENT;
}

我们看到 PRESENT 就是一个静态常量:使用 PRESENT 作为 HashMap 的 value 值,使用HashSet的开发者只需关注于需要插入的 key屏蔽了 HashMap 的 value

上图可以观察到每个Entryvalue都是 PRESENT 空对象,我们就不用再理会它了。

HashSet 在 HashMap 基础上实现,所以很多地方可以联系到 HashMap:

  • 底层数据结构:HashSet 也是采用数组 + 链表 + 红黑树实现
  • 线程安全性:由于采用 HashMap 实现,而 HashMap 本身线程不安全,在HashSet 中没有添加额外的同步策略,所以 HashSet 也线程不安全
  • 存入 HashSet 的对象的状态最好不要发生变化,因为有可能改变状态后,在集合内部出现两个元素o1.equals(o2),破坏了 equals()的语义。

LinkedHashSet

LinkedHashSet 的代码少的可怜,不信我给你我粘出来

少归少,还是不能闹,LinkedHashSet继承了HashSet,我们跟随到父类 HashSet 的构造方法看看

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    this.map = new LinkedHashMap(initialCapacity, loadFactor);
}

发现父类中 map 的实现采用LinkedHashMap,这里注意不是HashMap,而 LinkedHashMap 底层又采用 HashMap + 双向链表 实现的,所以本质上 LinkedHashSet 还是使用 HashMap 实现的。

LinkedHashSet -> LinkedHashMap -> HashMap + 双向链表

 

而 LinkedHashMap 是采用 HashMap双向链表实现的,这条双向链表中保存了元素的插入顺序。所以 LinkedHashSet 可以按照元素的插入顺序遍历元素,如果你熟悉LinkedHashMap,那 LinkedHashSet 也就更不在话下了。

关于 LinkedHashSet 需要注意几个地方:

  • 它继承了 HashSet,而 HashSet 默认是采用 HashMap 存储数据的,但是 LinkedHashSet 调用父类构造方法初始化 map 时是 LinkedHashMap 而不是 HashMap,这个要额外注意一下
  • 由于 LinkedHashMap 不是线程安全的,且在 LinkedHashSet 中没有添加额外的同步策略,所以 LinkedHashSet 集合也不是线程安全

TreeSet

TreeSet 是基于 TreeMap 的实现,所以存储的元素是有序的,底层的数据结构是数组 + 红黑树

而元素的排列顺序有2种,和 TreeMap 相同:自然排序和定制排序,常用的构造方法已经在下面展示出来了,TreeSet 默认按照自然排序,如果需要定制排序,需要传入Comparator

public TreeSet() { 
    this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) {
    this(new TreeMap<>(comparator));
}

TreeSet 应用场景有很多,像在游戏里的玩家战斗力排行榜

public class Player implements Comparable<Integer> {
    public String name;
    public int score;
    @Override
    public int compareTo(Student o) {
        return Integer.compareTo(this.score, o.score);
    }
}
public static void main(String[] args) {
    Player s1 = new Player("张三", 100);
    Player s2 = new Player("李四", 90);
    Player s3 = new Player("王五", 80);
    TreeSet<Player> set = new TreeSet();
    set.add(s2); set.add(s1); set.add(s3);
    System.out.println(set);
}
// [Student{name='王五', score=80}, Student{name='李四', score=90}, Student{name='张三', score=100}]

对 TreeSet 介绍了它的主要实现方式和应用场景,有几个值得注意的点。

  • TreeSet 的所有操作都会转换为对 TreeMap 的操作,TreeMap 采用红黑树实现,任意操作的平均时间复杂度为 O(logN)
  • TreeSet 是一个线程不安全的集合
  • TreeSet 常应用于对不重复的元素定制排序,例如玩家战力排行榜

注意:TreeSet判断元素是否重复的方法是判断compareTo()方法是否返回0,而不是调用 hashcode() 和 equals() 方法,如果返回 0 则认为集合内已经存在相同的元素,不会再加入到集合当中。

------------------------------------------与正文内容无关------------------------------------
 如果觉的文章写对各位读者老爷们有帮助的话,麻烦点赞加关注呗!作者在这拜谢了!

混口饭吃了!如果你需要Java 、Python毕设、商务合作、技术交流、就业指导、技术支持度过试用期。请在关注私信我,本人看到一定马上回复!

这是我全部文章所在目录,看看是否有你需要的,如果遇到觉得不对地方请留言,看到后我会查阅进行改正。

A乐神-CSDN博客

 

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A乐神

恭喜发财啊,老板,嘻嘻!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值