关注微信公众号:CodingTechWork,一起学习进步。

Set集合介绍

Set集合的概念

  Set集合类似于一个容器,程序把很多对象保存到Set集合中,Set集合对添加顺序不记录,当有重复的对象保存到Set集合时,不会新增后加的重复对象。

Set集合的特点

  1. Set集合无重复元素,add()方法添加相同元素时,返回false;
  2. Set集合add()方法不记录顺序;

HashSet类

HashSet介绍

  HashSet是按照哈希算法进行存储元素的,具有良好的查询和存取性能。

HashSet特点

  1. 集合元素值可以为null;
  2. 不保证元素的排列顺序,有可能排列顺序与添加顺序不同;
  3. 非同步集合,多线程访问HashSet时,是不安全的,需要通过同步代码保证同步。
  4. 元素不可重复相同,通过equals()hashCode()方法一起判断是否相同。

HashSet添加元素过程

add()方法:

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

  通过add()方法向HashSet存入元素时,HashSet会调用该对象的hashCode()方法获取hashCode值,然后根据hashCode值决定这个元素在HashSet的存储位置。如果有两个元素通过equals()方法返回true,但hashCode()方法返回值不同,则HashSet会存储到集合的不同位置,依旧可以成功添加该元素。

示例1:HashSet中元素对象的相同性依据

1)创建ClassA类,重写equals()方法

public class ClassA {@Overridepublic boolean equals(Object obj) {return true;}
}

2)创建ClassB类,重写hashCode()方法

public class ClassB {@Overridepublic int hashCode() {return 0;}
}

3)创建ClassC类,重写equals()hashCode()方法

public class ClassC {@Overridepublic int hashCode() {return 1;}@Overridepublic boolean equals(Object obj) {return true;}
}

4)测试主类:

public class DemoApplication {public static void main(String[] args) {// 创建集合HashSet hashSet = new HashSet();// 添加元素hashSet.add(new ClassA());hashSet.add(new ClassA());hashSet.add(new ClassB());hashSet.add(new ClassB());hashSet.add(new ClassC());hashSet.add(new ClassC());System.out.println("hashSet: ");hashSet.forEach(obj -> System.out.println(obj));}
}

5)运行结果:

hashSet:
com.example.andya.demo.bean.ClassB@0
com.example.andya.demo.bean.ClassB@0
com.example.andya.demo.bean.ClassC@1
com.example.andya.demo.bean.ClassA@1edf1c96
com.example.andya.demo.bean.ClassA@368102c8

  从上述运行结果可以看出:
1)HashSet集合添加顺序和集合内部元素顺序不一定相同;
2)HashSet添加元素时:

  • ClassA类重写了equals()方法,但是两个对象的hashCode()返回了不同的hashCode值,所以HashSet会将这两个对象保存在哈希表中的不同位置。
  • ClassB类重写了hashCode()方法,但是两个对象的equals()方法返回的是不同的对象地址,所以HashSet会将这两个对象保存到同一个位置,并通过链表链接。这种方式不建议有,因为集合中出现链式结构来存储相同hash值的元素时,查询速度会变慢,性能下降。
  • 只有ClassC是当作一个对象来添加到集合中,只有当equals()hashCode()方法都重写的时候,才可以作为判断对象是否相同的依据。

3)总结:若需要将某个类的对象保存到HashSet集合时,我们需要重写这个类的equals()hashCode()方法,只有保证了两个对象通过equals()方法返回true,hashCode()方法返回值相同时,才是相同对象。

重写hashCode()方法的准则

  1. 可重复性:程序运行时,同一个对象多次调用hashCode()方法返回的是相同值;
  2. 当两个对象通过equals()方法比较后返回的是true,则两个对象的hashCode()方法应返回相同的hash值;
  3. 对象中用作equals()方法比较标准的实例变量,都应该用于计算hashCode值;

HashSet集合为何查询速度快?

  我们回答这个问题前,先看一下HashSet添加元素的流程,使用add()方法添加元素时,HashSet会根据这个元素的hashCode值计算存储位置进行存储。当我们查询元素时,也是通过该元素的hashCode值快速定位到该元素在集合中的位置,其实HashSet底层是通过HashMap实现的。可以观察到源码:

public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, java.io.Serializable {private transient HashMap<E,Object> map;//构造方法public HashSet() {map = new HashMap<>();}public HashSet(Collection<? extends E> c) {map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));addAll(c);}//add方法public boolean add(E e) {return map.put(e, PRESENT)==null;}}

LinkedHashSet

LinkedHashSet介绍

  LinkedHashSet是HashSet子类,同样是根据元素的hashCode值判断元素的存储位置,且同时使用链表维护元素的顺序,给人的直观效果就是集合的顺序就是元素插入的顺序保存的。
  下面我们通过一组示例来看一下LinkedHashSet的保存元素次序效果。

LinkedHashSet示例

1)主类

public class DemoApplication {public static void main(String[] args) {LinkedHashSet linkedHashSet = new LinkedHashSet();linkedHashSet.add("1");linkedHashSet.add("2");linkedHashSet.add(3);System.out.println("旧的集合:" + linkedHashSet);linkedHashSet.remove("1");linkedHashSet.add(1);System.out.println("新的集合:" + linkedHashSet);}
}

2)运行结果

旧的集合:[1, 2, 3]
新的集合:[2, 3, 1]

  从上述运行结果中,我们可以看到LinkedHashSet的元素保存顺序即为添加元素的顺序。

TreeSet

TreeSet介绍

  TreeSet是SortedSet接口实现类,TreeSet是一种排序集合,该对象中只能添加一种类型元素,否则,会抛出java.lang.ClassCastException异常;
  与HashSet集合采用hash算法来决定元素的存储位置有些不一样,TreeSet是采用红黑树这种数据结构存储集合元素。

TreeSet方法

除了Collection集合的常用方法外,TreeSet集合还有如下常用方法:

  • Comparator<? super E> comparator():如果TreeSet采用定制排序,该方法返回定制排序所使用的Comparator,如果TreeSet采用自然排序,则返回null;
  • E first():返回集合中的第一个元素;
  • E last():返回集合中的最后一个元素;
  • E lower(E e):返回集合找找那个微语指定元素之前的元素(小于指定元素的最大元素)
  • E higher(E e):返回集合中位于指定元素之后元素(大于指定元素的最小元素)
  • SortedSet<E> subSet(E fromElement, E toElement):返回Set的子集合,从fromElement(包含)到toElement(不包含)
  • SortedSet<E> headSet(E toElement):返回此Set的子集,该子集是由小于toElement的元素组成;
  • SortedSet<E> tailSet(E toElement):返回此Set的子集,该子集是由大于toElement的元素组成;

示例

1)运行主类:

public class DemoApplication {public static void main(String[] args) {TreeSet treeSet = new TreeSet();treeSet.add(2);treeSet.add(1);treeSet.add(6);treeSet.add(3);treeSet.add(8);System.out.println(treeSet);//查看排序性treeSet.remove(3);treeSet.add(5);System.out.println(treeSet);System.out.println("first()方法: " + treeSet.first());System.out.println("last()方法: " + treeSet.last());System.out.println("lower()方法: " + treeSet.lower(5));System.out.println("higher()方法: " + treeSet.higher(5));System.out.println("subSet()方法: " + treeSet.subSet(2,6));System.out.println("headSet()方法: " + treeSet.headSet(5));System.out.println("tailSet()方法: " + treeSet.tailSet(5));}
}

2)运行结果:

[1, 2, 3, 6, 8]
[1, 2, 5, 6, 8]
first()方法: 1
last()方法: 8
lower()方法: 2
higher()方法: 6
subSet()方法: [2, 5]
headSet()方法: [1, 2]
tailSet()方法: [5, 6, 8]

  从上述运行结果可以看出,TreeSet不是根据添加元素的顺序进行排序,而是通过元素实际大小进行排序。

TreeSet的compareTo()方法详解

  TreeSet支持两种排序方式:自然排序自定义排序(定制排序)

自然排序

介绍:
  TreeSet是通过Comparable接口的compareTo(Object obj)方法比较元素之间的大小关系,然后将元素按照升序排列,即为自然排序。
实现原理:
  实现Comparable接口,并实现compareTo(Object obj)方法,返回一个整数值。当一个对象调用该方法与另一个对象进行比较时,如obj1.compareTo(obj2),该方法若返回0,则表明obj1和obj2这两个对象相等;若返回一个正整数,则表明obj1大于obj2;若返回一个负整数,则表明obj1小于obj2。然后根据红黑树结构寻找存储位置,当元素相同,若此时使用集合add()方法时,无法将新对象添加到集合内。
  TreeSet通过元素对应的类重写的compareTo(Object obj)方法来比较对象大小,就要求重写元素对象对应类的equals()方法时,需要保证该方法与compareTo(Object obj)方法保持一致的结果,当两个对象通过equals()方法返回true时,两个对象通过compareTo(Object obj)方法应该返回0。

示例
1)对象类

public class OnePerson implements Comparable<OnePerson>{private int age;private String name;public OnePerson(int age, String name) {this.age = age;this.name = name;}//重写equals()方法与compareTo()方法保持一致的效果@Overridepublic boolean equals(Object obj) {if(this == obj) {return true;}if(obj != null && obj.getClass() == OnePerson.class) {OnePerson onePerson = (OnePerson) obj;return onePerson.age == this.age;}return  false;}//重写compareTo()方法,以age作为排序比较标准@Overridepublic int compareTo(OnePerson o) {return this.age > o.age? 1 : this.age < o.age? -1 : 0;}@Overridepublic String toString() {return "OnePerson{" +"age=" + age +", name='" + name + '\'' +'}';}
}

2)运行类

public class DemoApplication {public static void main(String[] args) {TreeSet treeSet = new TreeSet();OnePerson onePerson1 = new OnePerson(5, "xiaoming");OnePerson onePerson2 = new OnePerson(3, "yaoyao");OnePerson onePerson3 = new OnePerson(8, "huangming");treeSet.add(onePerson1);treeSet.add(onePerson2);treeSet.add(onePerson3);treeSet.forEach(onePerson -> System.out.println(onePerson));}
}

3)运行结果

OnePerson{age=3, name='yaoyao'}
OnePerson{age=5, name='xiaoming'}
OnePerson{age=8, name='huangming'}

  从上述运行结果看出,对象类OnePerson添加到TreeSet时是按照age大小进行排序添加进集合。

自定义排序

介绍:
  自然排序是根据元素大小进行升序排列,如果需要实现降序等排列需要自定义排序,通过Comparator接口,该接口内有int compare(T o1, T o2)方法,使用该方法比较o1和o2的大小:若返回正整数,则表示o1>o2;若返回负整数,则表示o1<o2;若返回0,则表示o1==o2.
实现原理:
  创建TreeSet集合对象时,提供一个Comparator对象与该集合进行关联,在Comparator对象中实现集合元素的排序逻辑。

示例
1)对象类

public class OnePerson2{private int age;private String name;public OnePerson2(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "OnePerson2{" +"age=" + age +", name='" + name + '\'' +'}';}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}

2)运行主类:
Lambda方式

public class DemoApplication {public static void main(String[] args) {TreeSet treeSet = new TreeSet((o1, o2) ->{OnePerson2 onePerson1 = (OnePerson2)o1;OnePerson2 onePerson2 = (OnePerson2)o2;//根据OnePerson对象的age属性来判断大小,age越大,OnePerson对象越小,降序return  onePerson1.getAge() > onePerson2.getAge() ? -1: onePerson1.getAge() < onePerson2.getAge() ? 1 : 0;});treeSet.add(new OnePerson2(5, "xiaohong"));treeSet.add(new OnePerson2(2, "huangming"));treeSet.add(new OnePerson2(9, "yaoling"));treeSet.forEach(onePerson -> System.out.println(onePerson));}
}

compare()方式

public class DemoApplication {public static void main(String[] args) {TreeSet<OnePerson2> treeSet = new TreeSet(new Comparator<OnePerson2>() {@Overridepublic int compare(OnePerson2 o1, OnePerson2 o2) {return o1.getAge() > o2.getAge() ? -1: o1.getAge() < o2.getAge() ? 1 : 0;}});treeSet.add(new OnePerson2(5, "xiaohong"));treeSet.add(new OnePerson2(2, "huangming"));treeSet.add(new OnePerson2(9, "yaoling"));treeSet.forEach(onePerson -> System.out.println(onePerson));}
}

3)运行结果

OnePerson2{age=9, name='yaoling'}
OnePerson2{age=5, name='xiaohong'}
OnePerson2{age=2, name='huangming'}

  从上述运行结果看出,通过Comparator接口的lambda表达式实现了自定义降序。

EnumSet

EnumSet介绍

  EnumSet是一个枚举类的集合类,集合内的所有元素都必须是指定枚举类型的枚举值,枚举类型在创建EnumSet时显式或者隐式地指定;EnumSet也是有顺序的,该顺序是由枚举值在Enum类中的定义顺序决定。
  EnumSet不允许加入null元素,若尝试添加null元素,会抛出NullPointerException异常。

EnumSet常用方法

  • EnumSet<E> allOf(Class<E> elementType):创建一个包含指定枚举类全部枚举值的EnumSet集合。
  • EnumSet<E> noneOf(Class<E> elementType):创建一个元素类型为指定枚举类型的空EnumSet集合。
  • EnumSet<E> of(E first, E... rest):创建同一类型枚举值的一个或多个枚举值的EnumSet集合。
  • EnumSet<E> range(E from, E to):创建一个包含从from到to枚举值范围内所有枚举值的EnumSet集合。
  • EnumSet<E> complementOf(EnumSet<E> s):创建一个元素类型和指定的EnumSet集合相同的EnumSet集合,新EnumSet集合包含原EnumSet集合不包含的,新的EnumSet集合和原集合所有元素加起来是枚举类的所有枚举值。
  • EnumSet<E> copyOf(EnumSet<E> s):创建一个与指定EnumSet集合具有相同元素类型、相同元素值的EnumSet集合。
  • EnumSet<E> copyOf(Collection<E> c):使用一个普通集合创建EnumSet集合。但是该普通集合中的元素必须是枚举值,否则会抛出ClassCastException异常。

示例

1)枚举类

public enum ColourEnum {RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,PURPLE
}

2)运行主类

public class DemoApplication {public static void main(String[] args) {//allOf()获取枚举类的全部枚举值EnumSet<ColourEnum> colourEnumEnumSet = EnumSet.allOf(ColourEnum.class);System.out.println("allOf()方法:" + colourEnumEnumSet);//创建空集合,指定集合元素是ColourEnum类的枚举值EnumSet<ColourEnum> colourEnumEnumSet1 = EnumSet.noneOf(ColourEnum.class);colourEnumEnumSet1.add(ColourEnum.GREEN);colourEnumEnumSet1.add(ColourEnum.RED);System.out.println("noneOf()方法:" + colourEnumEnumSet1);//指定枚举值创建枚举集合EnumSet<ColourEnum> colourEnumEnumSet2 = EnumSet.of(ColourEnum.BLUE, ColourEnum.ORANGE);System.out.println("of()方法:" + colourEnumEnumSet2);//指定范围创建枚举集合EnumSet<ColourEnum> colourEnumEnumSet3 = EnumSet.range(ColourEnum.ORANGE, ColourEnum.INDIGO);System.out.println("range()方法:" + colourEnumEnumSet3);//补充枚举值,合集是整个ColourEnumEnumSet<ColourEnum> colourEnumEnumSet4 = EnumSet.complementOf(colourEnumEnumSet3);System.out.println("complementOf()方法:" + colourEnumEnumSet4);//复制枚举集合EnumSet<ColourEnum> colourEnumEnumSet5 = EnumSet.copyOf(colourEnumEnumSet4);System.out.println("copyOf()方法:" + colourEnumEnumSet5);}
}

3)运行结果

allOf()方法:[RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, PURPLE]
noneOf()方法:[RED, GREEN]
of()方法:[ORANGE, BLUE]
range()方法:[ORANGE, YELLOW, GREEN, BLUE, INDIGO]
complementOf()方法:[RED, PURPLE]
copyOf()方法:[RED, PURPLE]

各种Set集合的比较

性能比较

1)HashSet性能比TreeSet好(添加、查询),只有当需要保持排序的Set时,才使用TreeSet,否则使用HashSet。
2)HashSet比LinkedHashSet性能好(添加、删除)。
3)LinkedHashSet比HashSet性能好(遍历查询)。
4)EnumSet是所有Set性能最好的,但缺陷是只能保存同一个枚举类的枚举值作为集合元素。
5)HashSet、TreeSet和EnumSet都是线程不安全的,需要通过Collections工具类的synchronizedSet(Set<T> s)synchronizedSortedSet(SortedSet<T> s)方法来包装Set集合。

Java—Set集合详解(HashSet/LinkedHashSet/TreeSet/EnumSet)相关推荐

  1. Java Set集合详解及Set与List的区别

    Set是什么? Java中的Set集合是继承Collection的接口,是一个不包含重复元素的集合. 下图是Set集合的源码. Set和List都是以接口的形式来进行声明.Set主要包含三种存放数据类 ...

  2. 集合{LinkedHashMap TreeMap HashSet LinkedHashSet TreeSet 快速失败机制 ConcurrentHashMap CAS 多线程协同扩容}(二)

    目录标题 LinkedHashMap Map集合框架结构体系图 什么是LinkedHashMap Linked 链式 的意思 HashMap "哈希映射"的意思 LinkedHas ...

  3. java常用集合详解

    文章目录 一.常用集合大纲 1.常用集合框架及介绍 2.集合和数组的区别 二.Collection 集合(接口) 三.List集合(接口) 1.存储遍历方式 2.ArrayList(实现类) 3.Li ...

  4. Java Set集合详解

    一:set集合框架体系图 二:set集合介绍 Set接口继承了Collection接口,含有许多常用的方法. int size();返回集合的长度 boolean isEmpty();判断集合是否为空 ...

  5. Java—Map集合详解(HashMap/Hashtable/LinkedHashMap/Properties/TreeMap/WeakHashMap/IdentityHashMap/EnumMap)

    关注微信公众号:CodingTechWork,一起学习进步. Map Map集合介绍   Map(也称为字典.关联数组)是用于保存具有映射关系的数据,保存两组值,key和value,这两组值可以是任何 ...

  6. 【Java专题】Java泛型集合详解

    一.什么是泛型? 1.先来看不是泛型的ArrayList集合 ArrayList集合的底层是一个object[]数组,但是它跟数组比起来又有很多的优势,它可以存很多不同类型的数据.问题出现在数据被取出 ...

  7. Java—List集合详解

    关注微信公众号:CodingTechWork,一起学习进步. List集合介绍 List集合概述   List集合是一个元素有序(每个元素都有对应的顺序索引,第一个元素索引为0).且可重复的集合. L ...

  8. java map集合详解_JAVA干货:Map 集合详解

    java.util.Map集合 Map集合的特点: 1.Map集合是一个双列集合,一个元素包含两个值(一个key,一个value) 2.Map集合中的元素,key和value的数据类型可以相同,也可以 ...

  9. Java~Map集合详解

    文章目录 Map介绍 核心Map 内部哈希:哈希映射技术 常用方法 遍历Map Map介绍 java.util中的集合类包含Java中某些最常用的类,其中最常用的集合类就是List和Map. 并且在集 ...

最新文章

  1. JavaWeb总结(六)
  2. mysql 线程内存 回收_MySQL内存使用-线程独享
  3. JAVA线程池管理及分布式HADOOP调度框架搭建
  4. pgsql 前10条_白沙湾南片区11条新建道路最新进度及建成时间,已建成一条!还有一条将通车...
  5. python相关软件安装
  6. dbinfo.properties mysql_java 8.0Mysql 助手类
  7. jrebel gradle_JRebel适用于Gradle Spring Boot应用程序
  8. Android官方开发文档Training系列课程中文版:通知用户之大视图通知
  9. mysql的root密码的变更-进程的杀掉
  10. 网站服务器上加入三层开发的网站错误解决
  11. HDU 1257 - 最少拦截系统
  12. 高级编程和编程规范总结
  13. Repast Simphony——案例:僵尸感染人类
  14. PHP+MySQL实现留言板功能
  15. 浪曦云团的博客正式入驻CSDN
  16. Java 转换EXCEL Duble格式时间为指定格式日期字符串
  17. 固定table首行或尾行
  18. 基于订单号可重入的交易系统接口设计探讨
  19. 1.1.6 LSDB同步
  20. 计算机算法常用术语中英对照

热门文章

  1. Ubuntu系统显卡驱动、CUDA、CUDNN安装(二CUDA和CUDNN)
  2. wampserver的phpmyadmin(默认3306)端口如何更改为3307?
  3. 配置Sourcetree实现代码托管、分支合并、分支删除、版本控制 - 教程篇
  4. element-UI组件el-button样式覆写 - 生效篇
  5. 给网站logo添加css帅气亮光扫过特效 附教程
  6. 2019计算机应用基础期末考试试题,2019-2020年度公共课计算机应用基础考试试题附答案...
  7. 时间格式化,常用时间,格式
  8. 视频会议系统OpenMeetings v5.1.0源码
  9. Java开发必看JPA概念大全
  10. 雪豹玩家必备网站推荐: