前言

大家好啊,我是汤圆,今天给大家带来的是《Java中的集合Set - 入门篇》,希望对大家有帮助,谢谢

简介

前面介绍了集合List映射Map,最后再简单介绍下集合Set,相关类如下图所示

正文

Set从外面看像List(都是存储单一数据的集合),只不过存储的数据不会有重复

但是里面却是Map映射(因为它内存存储是基于Map结构实现),这也是为什么把Set放到Map后面来说的原因。

Set和Map有什么关系呢?

因为Map的键不会有重复,所以Set就利用了Map的这个特点,将其作为内部成员变量来使用

比如我们看下HashSet内部的源码,可以看到,基本上所有操作都是基于其内部的成员变量HashMap进行的


  1. private transient HashMap<E,Object> map;
  2. // 这个常量只是为了适配Map的键值对结构,无实际意义
  3. private static final Object PRESENT = new Object();
  4. public HashSet() {
  5. map = new HashMap<>();
  6. }
  7. public int size() {
  8. return map.size();
  9. }
  10. public boolean contains(Object o) {
  11. return map.containsKey(o);
  12. }
  13. public boolean add(E e) {
  14. return map.put(e, PRESENT)==null;
  15. }
  16. public boolean remove(Object o) {
  17. return map.remove(o)==PRESENT;
  18. }

Set的种类

Set的种类类似Map

Set主要有三种类型:HashSet(常用)、TreeSet(树形结构)、LinkedHashSet(前两者的结合)

我们先来看一下Set接口主要的几个方法:

  • boolean add(E e):往Set中添加元素
  • boolean contains(Object o):查询Set是否包含指定对象
  • boolean remove(Object o):从Set中删除指定对象
  • int size():返回Set的元素数量

下面我们简单看下三者的区别

HashSet TreeSet LinkedHashSet
访问速度 适中
元素是否有序 无序 有序,默认升序 有序,默认按插入的顺序
适用场景 为快速查询而设计(用的最多) 需要排序的场景 需要保证查询和插入顺序一致的场景

接下来我们以HashSet为例,来介绍Set接口

HashSet

HashSet是一个无序集合

因为它内部是基于HashMap实现

上面的源码我们有看到,HashSet每插入一个元素,就将该元素作为内部hashMap的key,然后常量Object作为hashMap的value,存储到hashMap中

  • 如果元素的hash值没有重复,就按照数组的方式依次排列;
  • 如果hash值有重复的,就添加到已有的值对后面,形成链表结构;

整体结构 如下图所示

下面用代码示范一下

  1. public class SetDemo {
  2. public static void main(String[] args) {
  3. // 初始化
  4. Set<Integer> set = new HashSet<>();
  5. // 添加元素
  6. set.add(10);
  7. // 元素数量
  8. int size = set.size();
  9. System.out.println(size);
  10. // 查询元素是否存在
  11. boolean isContain = set.contains(10);
  12. System.out.println(set);
  13. // 删除
  14. set.remove(10);
  15. System.out.println(set);
  16. }
  17. }

TreeSet

TreeSet在插入的时候,可以按照元素进行排序(默认升序)

它适合用在排序比较多的场景,性能会比HashSet差一些

下面用代码示范一下(重点要来了)

  1. public class SetDemo {
  2. public static void main(String[] args) {
  3. // TreeSet
  4. B b = new B();
  5. // 初始化
  6. Set<B> set2 = new TreeSet<>();
  7. // 添加元素
  8. set2.add(b);
  9. // 元素数量
  10. int size2 = set2.size();
  11. System.out.println(size2);
  12. // 查询元素是否存在
  13. boolean isContain2 = set2.contains(b);
  14. System.out.println(set2);
  15. // 删除
  16. set2.remove(b);
  17. System.out.println(set2);
  18. }
  19. }
  20. class B{
  21. }

这段代码看似一切正常,实则暗藏玄机

如果你运行这段代码,会报出下面的错误提示:类B不能转换为Comparable。

可是为什么要转换呢?我也没有转换啊

那是因为内部自动转换了

TreeSet啥时候会自动将元素类转为Comparable呢?

是在你插入第一个数据开始,内部就已经开始在做比较了(第一次先自己跟自己做比较,目的就是检查你这个数据有没有实现Comparable接口);

后面每插一个数据,都要从根节点开始挨个比较排序

这其实也算也是TreeSet内部排序的工作原理

所以上面这段代码需要让B实现Comparable接口,改后如下所示

  1. public class SetDemo {
  2. public static void main(String[] args) {
  3. // TreeSet
  4. B b = new B();
  5. // 初始化
  6. Set<B> set2 = new TreeSet<>();
  7. // 添加元素
  8. // 此时运行没问题
  9. set2.add(b);
  10. // 元素数量
  11. int size2 = set2.size();
  12. System.out.println(size2);
  13. // 查询元素是否存在
  14. boolean isContain2 = set2.contains(b);
  15. System.out.println(set2);
  16. // 删除
  17. set2.remove(b);
  18. System.out.println(set2);
  19. }
  20. }
  21. // 这里实现了Comparable
  22. class B implements Comparable{
  23. @Override
  24. public int compareTo(Object o) {
  25. return this.hashCode()>o.hashCode() ? 1 : -1;
  26. }
  27. }

此时运行就没问题了

那为什么TreeMap没有这个问题呢?

其实TreeMap也有这个问题,只是TreeMap的键key一般都是字符串或者整型,而字符串和整型对象内部已经实现了Comparable接口,所以问题不大(因为用自定义对象做键key的毕竟还是少数)

LinkedHashSet

LinkedHashSet拥有HashSet的大部分优点,且保证了插入的顺序,使得在查询的时候,可以按照插入的顺序依次读取(原理是链表)

这里要注意一点:在Java程序语言设计中,所有的链表都是双向链表,即每个节点还存放着一个指向前节点的引用

大致的结构图如下所示(之前的LinkedHashMap忘记贴图了,跟这个类似,只是元素内容不同):

三者的排序比较

参考上一篇映射Map,Set排序比较表现出来的行为与Map一致

总结

Set一般用到的有HashSet,TreeSet,LinkedHashSet,内部都是无重复元素

  • HashSet的插入和访问都很快,但是内部是无序排列

  • TreeSet的插入和访问都很慢,但是内部是有序排列,默认按升序排列

  • LinkedHashSet拥有HashSet的大部分优点,而且还可以按照元素插入的顺序来访问元素,但是性能会比HashSet差

后记

最后,感谢大家的观看,谢谢

Java中的集合Set - 入门篇的更多相关文章

  1. Java中的集合List - 入门篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的集合List - 入门篇>,希望对大家有帮助,谢谢 简介 说实话,Java中的集合有很多种,但是这里作为入门级别,先简单介绍第一种 ...

  2. Java中的映射Map - 入门篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的映射Map - 入门篇>,希望对大家有帮助,谢谢 简介 前面介绍了集合List,这里开始简单介绍下映射Map,相关类如下图所示 正 ...

  3. Java中的IO流 - 入门篇

    前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的IO流-入门篇>,希望对大家有帮助,谢谢 由于Java的IO类有很多,这就导致我刚开始学的时候,感觉很乱,每次用到都是上网搜,结果 ...

  4. Java中的集合框架-Collections和Arrays

    上一篇<Java中的集合框架-Map>把集合框架中的键值对容器Map中常用的知识记录了一下,本节记录一下集合框架的两个工具类Collections和Arrays 一,Collections ...

  5. Java中的集合框架-Map

    前两篇<Java中的集合框架-Commection(一)>和<Java中的集合框架-Commection(二)>把集合框架中的Collection开发常用知识点作了一下记录,从 ...

  6. Java中的集合框架-Collection(二)

    上一篇<Java中的集合框架-Collection(一)>把Java集合框架中的Collection与List及其常用实现类的功能大致记录了一下,本篇接着记录Collection的另一个子 ...

  7. Java中的集合(四)PriorityQueue常用方法

    Java中的集合(四)PriorityQueue常用方法 PriorityQueue的基本概念等都在上一篇已说明,感兴趣的可以点击 Java中的集合(三)继承Collection的Queue接口 查看 ...

  8. 万字长文深入理解java中的集合-附PDF下载

    目录 1. 前言 2. List 2.1 fail-safe fail-fast知多少 2.1.1 Fail-fast Iterator 2.1.2 Fail-fast 的原理 2.1.3 Fail- ...

  9. Java 中的集合接口——List、Set、Map

    Java 中的集合接口——List.Set.Map 什么叫集合:集合就是Java API所提供的一系列类的实例,可以用于动态存放多个对象.这跟我们学过的数组差不多,那为什么我们还要学集合,我们看看数组 ...

随机推荐

  1. jest all in one

    jest all in one ES Modules & TypeScript & React https://github.com/xgqfrms/FEAT/tree/master/ ...

  2. LeetCode & linked list bug

    LeetCode & linked list bug add-two-numbers shit test /** * Definition for singly-linked list. * ...

  3. funny 生成器

    funny 生成器 https://www.zhihu.com/question/380741546/answer/1190570384 举牌小人生成器 https://small-upup.upup ...

  4. css3 units & 1 turn

    css3 units & 1 turn One full circle is 1turn, 180deg === 0.5turn degrees, gradians, radians, tur ...

  5. es6 & map & set

    es6 & map & set Map & WeakMap https://developer.mozilla.org/en-US/docs/Web/JavaScript/Re ...

  6. git alias & zsh

    git alias & zsh VPN & git work tree # git pull === gp ➜ .git git:(feature/select-seat-system ...

  7. Flutter 使用Tabbar不要Title

    原文 Demo 1 import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp ext ...

  8. 一文助你了解NGK商城

    按照NGK的发展逻辑,将会在2021年上半年上线链商商城,解决传统消费行业真伪难辨的弊端,之后,将会推出小额支付功能,让NGK真正成为结算中的数字资产,目前两者落地性应用已经在开发内测阶段中,期初的部 ...

  9. 谁能成为数据储存领域领头羊?永久数据存储--NGK的终极使命!

    区块链的目的是永远存储交易网络的历史.NGK技术团队能够永久存储其去中心化账本的副本.这是其日后能进行审计关键.一些著名的团队,如Solana和SKALE,现在正在为此与NGK进行最后的集成,我们预计 ...

  10. [转]基于ROS平台的移动机器人-4-通过ROS利用键盘控制小车移动

    原文出处: https://blog.csdn.net/Forrest_Z/article/details/55002484 准备工作 1.下载串口通信的ROS包 (1)cd ~/catkin_ws/ ...