java集合(4)- java中HashSet详解
HashSet 的实现
对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSet 的源代码,可以看到如下代码:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
// 使用 HashMap 的 key 保存 HashSet 中所有元素
private transient HashMap<E,Object> map;
// 定义一个虚拟的 Object 对象作为 HashMap 的 value
private static final Object PRESENT = new Object();
...
// 初始化 HashSet,底层会初始化一个 HashMap
public HashSet()
{
map = new HashMap<E,Object>();
}
// 以指定的 initialCapacity、loadFactor 创建 HashSet
// 其实就是以相应的参数创建 HashMap
public HashSet(int initialCapacity, float loadFactor)
{
map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity)
{
map = new HashMap<E,Object>(initialCapacity);
}
HashSet(int initialCapacity, float loadFactor, boolean dummy)
{
map = new LinkedHashMap<E,Object>(initialCapacity
, loadFactor);
}
// 调用 map 的 keySet 来返回所有的 key
public Iterator<E> iterator()
{
return map.keySet().iterator();
}
// 调用 HashMap 的 size() 方法返回 Entry 的数量,就得到该 Set 里元素的个数
public int size()
{
return map.size();
}
// 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,
// 当 HashMap 为空时,对应的 HashSet 也为空
public boolean isEmpty()
{
return map.isEmpty();
}
// 调用 HashMap 的 containsKey 判断是否包含指定 key
//HashSet 的所有元素就是通过 HashMap 的 key 来保存的
public boolean contains(Object o)
{
return map.containsKey(o);
}
// 将指定元素放入 HashSet 中,也就是将该元素作为 key 放入 HashMap
public boolean add(E e)
{
return map.put(e, PRESENT) == null;
}
// 调用 HashMap 的 remove 方法删除指定 Entry,也就删除了 HashSet 中对应的元素
public boolean remove(Object o)
{
return map.remove(o)==PRESENT;
}
// 调用 Map 的 clear 方法清空所有 Entry,也就清空了 HashSet 中所有元素
public void clear()
{
map.clear();
}
...
}
由上面源程序可以看出,HashSet 的实现其实非常简单,它只是封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
掌握上面理论知识之后,接下来看一个示例程序,测试一下自己是否真正掌握了 HashMap 和 HashSet 集合的功能。
class Name
{
private String first;
private String last; public Name(String first, String last)
{
this.first = first;
this.last = last;
} public boolean equals(Object o)
{
if (this == o)
{
return true;
} if (o.getClass() == Name.class)
{
Name n = (Name)o;
return n.first.equals(first)
&& n.last.equals(last);
}
return false;
}
} public class HashSetTest
{
public static void main(String[] args)
{
Set<Name> s = new HashSet<Name>();
s.add(new Name("abc", "123"));
System.out.println(
s.contains(new Name("abc", "123")));
}
}
上面程序中向 HashSet 里添加了一个 new Name("abc", "123") 对象之后,立即通过程序判断该 HashSet 是否包含一个 new Name("abc", "123") 对象。粗看上去,很容易以为该程序会输出 true。
实际运行上面程序将看到程序输出 false,这是因为 HashSet 判断两个对象相等的标准除了要求通过 equals() 方法比较返回
true 之外,还要求两个对象的 hashCode() 返回值相等。而上面程序没有重写 Name 类的 hashCode() 方法,两个
Name 对象的 hashCode() 返回值并不相同,因此 HashSet 会把它们当成 2 个对象处理,因此程序返回 false。
由此可见,当我们试图把某个类的对象当成 HashMap 的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的
equals(Object obj) 方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的
hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode()
返回值的关键属性,都应该用于作为 equals() 比较的标准。
如下程序就正确重写了 Name 类的 hashCode() 和 equals() 方法,程序如下:
class Name
{
private String first;
private String last;
public Name(String first, String last)
{
this.first = first;
this.last = last;
}
// 根据 first 判断两个 Name 是否相等
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o.getClass() == Name.class)
{
Name n = (Name)o;
return n.first.equals(first);
}
return false;
} // 根据 first 计算 Name 对象的 hashCode() 返回值
public int hashCode()
{
return first.hashCode();
} public String toString()
{
return "Name[first=" + first + ", last=" + last + "]";
}
} public class HashSetTest2
{
public static void main(String[] args)
{
HashSet<Name> set = new HashSet<Name>();
set.add(new Name("abc" , "123"));
set.add(new Name("abc" , "456"));
System.out.println(set);
}
}
上面程序中提供了一个 Name 类,该 Name 类重写了 equals() 和 toString() 两个方法,这两个方法都是根据 Name 类的 first 实例变量来判断的,当两个 Name 对象的 first 实例变量相等时,这两个 Name 对象的 hashCode() 返回值也相同,通过 equals() 比较也会返回 true。
程序主方法先将第一个 Name 对象添加到 HashSet 中,该 Name 对象的 first
实例变量值为"abc",接着程序再次试图将一个 first 为"abc"的 Name 对象添加到 HashSet 中,很明显,此时没法将新的
Name 对象添加到该 HashSet 中,因为此处试图添加的 Name 对象的 first 也是" abc",HashSet 会判断此处新增的
Name 对象与原有的 Name 对象相同,因此无法添加进入,程序在①号代码处输出 set 集合时将看到该集合里只包含一个 Name
对象,就是第一个、last 为"123"的 Name 对象。
java集合(4)- java中HashSet详解的更多相关文章
- Java程序在内存中运行详解
目录 Java程序在内存中运行详解 一.JVM的内存分布 二.程序执行的过程 三.只有一个对象时的内存图 四.两个对象使用同一个方法的内存图 五.两个引用指向同一个对象的内存图 六.使用对象类型作为方 ...
- java集合的方法及使用详解
一.java集合的分类及相互之间的关系 Collection接口:向下提供了List和Set两个子接口 |------List接口:存储有序的,存储元素可以重复 |------ArrayList(主要 ...
- Java 集合Collection与List的详解
1.什么是集合 存储对象的容器,面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,存储对象,集合是存储对象最常用的一种方式. 集合的出现就是为了持有对象.集合中可以存储任意类型的 ...
- java中HashSet详解(转)
HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSe ...
- java中HashSet详解
HashSet 的实现 对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层采用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,查看 HashSe ...
- Java连接数据库的4中方式详解
Java连接数据库的方式有多种:根据所需要的不同数据库驱动分,分为四种: 1:1类驱动.这就是JDBC-ODBC桥的方式. 但这种方式不适合程序的重用与维护,不推荐使用.需要数据库的ODBC驱动. 2 ...
- java集合(2)- java中HashMap详解
java中HashMap详解 基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了非同步和允许使用 null 之外,HashMap 类与 H ...
- 【转】 java中HashMap详解
原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Jav ...
- java中HashMap详解(转)
java中HashMap详解 博客分类: JavaSE Java算法JDK编程生活 HashMap 和 HashSet 是 Java Collection Framework 的两个重要成 ...
随机推荐
- com.android.ide.common.process.PrecessException:org.gradle.process....finished with non-zero exit value 1
1.问题描述: 如图,在生成apk文件时出现如下错误, 2.原因分析: 我在网上搜了很多类似的问题,但试了又试也没有解决.然后我想上次编译时都没有出错,应该是最近的操作导致的错误. 3.解决办法: 把 ...
- Python javascript操作DOM
文档对象模型(Document Object Model,DOM)是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式.我们最为关心的是,DOM把 ...
- [Oracle]审计Audit
1.Audit的概念 Audit是监视和记录用户对数据库进行的操作,以供DBA进行问题分析.利用Audit功能,可以完成以下任务: 监视和收集特定数据库活动的数据.例如管理员能够审计哪些表被更新,在某 ...
- MySQL5.7下修改root密码
本地安装了个mysql5.7,发现修改root密码和以往有区别了,特此记录下 进入mysql 更改密码: mysql> update mysql.user set authentication ...
- struts2 之 struts2数据校验
1. 数据校验一般分为2类:前端的校验(js校验),后端的校验(java代码):实际开发中大部分情况下都是采用js校验.在对数据安全要求较高的情况下可能会采用后端验证. 2. Struts2提供了后 ...
- angular购物车
<body ng-app> <div class="container" ng-controller="carController"> ...
- JQ实现选中以后就左右移动
<head> <meta charset="utf-8" /> <title>select_option移动</title> < ...
- SAP RFC函数远程调试跟踪管理软件
最近在搞OA系统与sap的接口开发,接口太多老是和.net的开发人员打嘴仗,为了避免不必要的纠结,自己做了一个rfc的调试工具,有些问题调试起来也比较容易了.程序是delphi开发的,为了保证程序可以 ...
- JAVA Struts2 搭建
java struts 2搭建 1.web工程 2.将struts2 用到的jar包,拷贝到webcontent/webinf/lib文件夹.下 3.webcontent 下的web.xml 下 ...
- AOJ/初等排序习题集
ALDS1_1_D-MaximumProfit. Codes: //#define LOCAL #include <cstdio> #include <algorithm> u ...