在上一讲中介绍了散列映射表HashMap和树映射表TreeMap,知道了HashMap的底层实现机制。这一讲将介绍Set接口和实现类:HashSet和TreeSet。由于HashSet的实现是基于HashMap的,TreeSet的实现是基于TreeMap的,所以这里不做过多底层的讨论,毕竟这部分已经在Java集合(四):Map映射中讨论过了。

1 散列表与Set接口

链表和数组可以按照人们的意愿排列元素的顺序。但是,如果想要查看某个指定的元素,但却忘了它的位置,就需要访问所有的元素,直到找到为止。如果集合中的元素很多,将会消耗很长时间。如果不在意元素的顺序,可以有几种能够快速查找元素的数据结构。但是缺点是不能控制元素的顺序。它们将按照有利于其操作目的的原则组织数据。

有一种常见的数据结构,就是散列表(hash table)。散列表可以根据每个对象计算一个整数,称为散列码(hash code)。不同的对象产生不同的散列码。

在Java中,散列表用链表数组实现。每个列表称为桶,这个已经在Map映射中介绍了。

散列表的特点就是:元素没有顺序,元素不能重复。

Set接口的定义如下:

public interface java.util.Set<E> extends java.util.Collection<E> {public abstract int size();public abstract boolean isEmpty();public abstract boolean contains(java.lang.Object);public abstract java.util.Iterator<E> iterator();public abstract java.lang.Object[] toArray();public abstract <T> T[] toArray(T[]);public abstract boolean add(E);public abstract boolean remove(java.lang.Object);public abstract boolean containsAll(java.util.Collection<?>);public abstract boolean addAll(java.util.Collection<? extends E>);public abstract boolean retainAll(java.util.Collection<?>);public abstract boolean removeAll(java.util.Collection<?>);public abstract void clear();public abstract boolean equals(java.lang.Object);public abstract int hashCode();public java.util.Spliterator<E> spliterator();
}

这些方法的含义也很简单。

2 HashSet类

Java集合类库中提供了HashSet类实现了Set接口。这个类的底层是使用HashMap实现的:

private transient HashMap<E,Object> map;// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

我们知道,HashMap是一个键值对,而HashSet存储的不是键值对,因此为了使用HashMap存储HashSet的元素,就需要构造一个键值对。可以使用需要存储在HashSet中的元素作为键,而上面的PRESENT作为值,就构成了一个键值对,这样就可以存在HashMap中了。

也就是说,HashSet与HashMap的原理一样,不同的是HashSet的值都一样,都是PRESENT。

由于在前一节中已经详细介绍了HashMap的原理,这里不再叙述了。只说一下HashSet的使用。

下面的代码从System.in中读取单词,然后将它们添加到HashSet中,再打印出所有的单词。由于HashSet中不存储相同的元素,所以打印出来的单词是不重复的。运行这个程序时使用下面的命令行:

java SetTest < alice.txt

这样就把alice.txt作为输入,程序就会读取所有的单词。代码如下:

import java.util.*;public class SetTest
{public static void main(String[] args){Set<String> words = new HashSet<>(); // HashSet implements Setlong totalTime = 0;Scanner in = new Scanner(System.in);while (in.hasNext()){String word = in.next();long callTime = System.currentTimeMillis();words.add(word);callTime = System.currentTimeMillis() - callTime;totalTime += callTime;}Iterator<String> iter = words.iterator();for (int i = 1; i <= 20 && iter.hasNext(); i++)System.out.println(iter.next());System.out.println(". . .");System.out.println(words.size() + " distinct words. " + totalTime + " milliseconds.");}
}

结果如下:

可以看见,一共有5392个不同的单词。

3 TreeSet类

TreeSet和HashSet类似,不过,它比HashSet有所改进。TreeSet是一个有序集合,可以以任意顺序将元素插入到集合中。在对集合进行遍历时,每个值将自动按照排序后的顺序呈现。例如,假设插入三个字符串,然后访问添加的所有元素:

SortedSet<String> sorter=new TreeSet<>();
sorter.add("B");
sorter.add("A");
sorter.add("C");
for(String s:sorter)System.out.println(s);

结果是:A B C

TreeSet的底层是使用TreeMap实现的,是一个红黑树。每次添加一个元素到树中时,都被放置在正确的排序位置上。因此,迭代器总是以排好序的顺序访问每个元素。

将一个元素添加到树中要比添加到一个散列表中要慢,但是,与将元素添加到数组中或链表中要快。如果树中一共有n个元素,将元素插入到正确位置的时间为logn。

与TreeMap一样,构造一个TreeSet也需要一个比较器,可以使用默认的比较器,也可以使用自己的比较器。使用自己的比较器时,需要给TreeSet的构造器传递一个Comparator对象。

下面的程序创建了两个Item对象的树集。第一个按照部件编号排序,这是Item对象的默认顺序。第二个通过使用一个定制的比较器来按照描述信息排序:

import java.util.*;public class TreeSetTest
{  public static void main(String[] args){  SortedSet<Item> parts = new TreeSet<>();parts.add(new Item("Toaster", 1234));parts.add(new Item("Widget", 4562));parts.add(new Item("Modem", 9912));System.out.println(parts);SortedSet<Item> sortByDescription = new TreeSet<>(newComparator<Item>(){  public int compare(Item a, Item b){  String descrA = a.getDescription();String descrB = b.getDescription();return descrA.compareTo(descrB);}});sortByDescription.addAll(parts);System.out.println(sortByDescription);}
}
import java.util.*;/*** An item with a description and a part number.*/
public class Item implements Comparable<Item>
{private String description;private int partNumber;/*** Constructs an item.* * @param aDescription*           the item's description* @param aPartNumber*           the item's part number*/public Item(String aDescription, int aPartNumber){description = aDescription;partNumber = aPartNumber;}/*** Gets the description of this item.* * @return the description*/public String getDescription(){return description;}public String toString(){return "[\n\tdescripion=" + description + ",\n\tpartNumber=" + partNumber +"\n]\n";}public boolean equals(Object otherObject){if (this == otherObject) return true;if (otherObject == null) return false;if (getClass() != otherObject.getClass()) return false;Item other = (Item) otherObject;return Objects.equals(description, other.description) && partNumber == other.partNumber;}public int hashCode(){return Objects.hash(description, partNumber);}public int compareTo(Item other){return Integer.compare(partNumber, other.partNumber);}
}

结果如下:

Java集合(五):Set集相关推荐

  1. Java——集合(合集,简单的概括)

    JAVA --集合 集合 概念: 1 .Collection接口与Iterator接口 2 .Collection<>接口 3 . List<>接口 3 .1 ArrayLis ...

  2. java集合面试锦集

    Java集合框架(例如基本的数据结构)里包含了最常见的Java常见面试问题.很好地理解集合框架,可以帮助你理解和利用Java的一些高级特性.下面是面试Java核心技术的一些很实用的问题. Q:最常见的 ...

  3. java基础集合数组间的转换(java集合五)

    数组转变成集合 public static void main(String[] args) {// 如果数组中元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的元素,// 如果数组中的元素 ...

  4. Java集合(一):Java集合概述

    注:本文基于JDK 1.7 1 概述 Java提供了一个丰富的集合框架,这个集合框架包含了许多接口.虚拟类和实现类.这些接口和类提供了丰富的功能,能够满足基本的聚合需求.下图就是这个框架的整体结构图: ...

  5. java泛型 ppt_第7章-Java集合与泛型-精品课件(PPT)-精品课件(PPT)最新版

    <第7章 -Java集合与泛型-精品课件(PPT)-精品课件(PPT).ppt>由会员分享,可免费在线阅读全文,更多与<第7章 -Java集合与泛型-精品课件(PPT)-精品课件(P ...

  6. java list有序还是无序_牛批!2w字的Java集合框架面试题精华集(2020最新版),赶紧收藏。...

    一个多月前,作者和一些小伙伴决定做一系列的 Java 知识点常见重要问题的小册,方便用来夯实基础!小册的标准就一个,那就是:取精华,取重点.每一本小册,我们都会充分关注我们所总结的知识点是否达到这个标 ...

  7. 「Java面试题精华集」1w字的Java集合框架篇(2022最新版)附PDF版

    昨天晚上终于把 Java 集合框架部分的的知识点肝完了,转换成 PDF 一共 25 页,后台回复:"面试突击" 即可免费获取下载地址(同样提供了夜间阅读版本). 集合概述 Java ...

  8. java集合——树集(TreeSet)+对象的比较

    [0]README 0.1) 本文描述转自 core java volume 1, 源代码为原创,旨在理解 java集合--树集(TreeSet)+对象的比较 的相关知识: 0.2) for full ...

  9. java集合——数组列表(ArrayList)+散列集(HashSet)

    [0]README 0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java集合--数组列表(ArrayList)+散列集(HashSet) 的相关知识: 0.2 ...

最新文章

  1. JS 正则表达式 0.001 ~99.999
  2. 算法学习之路|D进制的A+B
  3. py2数据分析_利用数据
  4. Keil 汇编窗口无法设置断点,disassembly显示错误,Keil汇编解析错误
  5. python每隔一段时间保存网页内容_利用Python轻松爬取网页题库答案!教孩子不怕尴尬了!...
  6. Pytho-SyntaxError: Non-ASCII character '\xe7' in file解决方法
  7. Latex 经常见到的问题和解决方法
  8. 什么是用户画像?如何构建用户画像?
  9. 解决错误:Main application must be in the list of ap...
  10. Python3有哪几种数据类型?
  11. 【C++】Visual Studio教程(一)-概述
  12. java大量浮点数如何作比较,Java如何正确比较浮点数
  13. java linux命令远程执行_java执行远程服务器上的shell命令
  14. MYSQL查询近一年 近一月 近一周 今天数据 没有数据返回0 按时间有序返回数据
  15. a4如何打印双面小册子_小册子打印
  16. 再启程,Service Mesh 前路虽长,尤可期许
  17. Spring Cloud Eureka服务注册中心 多节点搭建(学习总结)
  18. html rgb 颜色转换,将RGBA颜色转换为HTML颜色代码
  19. php 数据库 编程,php数据库编程(mysql mysqli pdo)
  20. R费希尔精确检验(Fisher‘s exact test)

热门文章

  1. Delphi与Windows 7下的用户账户控制(UAC)机制
  2. HDU-1518 Square dfs+剪枝
  3. int定义源码 python_python学习(第一章)
  4. python 三维绘图库_Python第三方库matplotlib(2D绘图库)入门与进阶
  5. 计算机等级考试试题4,计算机等级考试二级模拟试题4
  6. firewallD卸载Linux,在Ubuntu 18.04/16.04系统上安装和使用Firewalld的方法
  7. 乐高计算机发展史教程,【乐高产品发展史特别篇】乐高恐龙发展史
  8. 求10以内平均数的c语言,求助 给小学生出题,自己选加减乘除 做10题 10以内的数 然后统计分...
  9. 汇编写java模块_java – maven汇编插件moduleset源指令不包括任何文件,不符合附带的模块...
  10. NXP KW38开发杂记(一)MCUXpress 运行进入NMI_Handler