java源码分析之HashSet和LinkedHashSet
2016-01-07 14:14
316 查看
学习HashSet的时候需要先了解HashMap,如果不是很了解HashMap的请看HashMap
Java容器类的用途是“保存对象”,分为两类:Map——存储“键值对”组成的对象;Collection——存储独立元素。Collection又可以分为List和Set两大块。List保持元素的顺序,可以有相同的元素,而Set不能有重复的元素。
首先对Set接口进行简要的说明。
存入Set的每个元素必须是惟一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set不保证维护元素的次序。Set与Collection有完全一样的接口。
在没有其他限制的情况下需要Set时应尽量使用HashSet,因为它对速度进行了优化。
下面是HashSet的定义:
HashSet继承了AbstractSet,实现了Set接口。其实AbstractSet已经实现Set接口了。AbstractSet继承自AbstractCollection,而AbstractCollection实现了Collection接口的部分方法,而Set接口和Collection接口完全一致,所以AbstractSet只是实现了AbstractCollection没有实现的Set接口的方法和重写了部分AbstractCollection已经实现的方法。
下面是HashSet定义的属性:
为什么会有一个HashMap
上面的构造方法都很简单,只有构造方法二中调用了addAll(Collection
看add(E e)方法只是调用了HashMap(构造方法中提供了创建LinkedHashMap的方式,但是LinkedHashMap是继承HashMap的,put方法也是调用HashMap的put方法)的put方法将e当做Key,PERSENT当做Value加入到map中并根据返回值判断是否添加成功。
因为HashMap的put方法在Key已经存在的情况下返回的是对应的Value值,若Key不存在则返回的是null,所以根据返回的是null可以确定新元素被添加到HashSet中了,如果返回的是其他值则说明Key已经存在,即元素已经在HashSet中已经存在,add(E e)返回的结果为false。虽然add(E e)返回false说明了HashSet添加元素失败,但实际上其中的map中的内容已经被替换,原先的值被PERSENT代替。
如果原先的值就是null呢?其实不用考虑这个问题,因为通过HashSet添加的元素,Value的内容都是PERSENT,不会出现null的情况。
LinkedHashSet源码分析
LinkedHashSet具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按照元素的插入次序显示。(LinkedHashMap中还可以按照LRU先后顺序显示),
看LinkedHashSet的内容。
LinkedHashSet继承自HashSet,HashSet基于HashMap实现,看LinkedHashSet类只是定义了四个构造方法,也没看到和链表相关的内容,为什么说LinkedHashSet内部使用链表维护元素的插入顺序(插入的顺序)呢?
//先看下HashSet的中的第五个构造函数
如果还有对LinkedHashMap不明白的,请看LinkedHashMap
Java容器类的用途是“保存对象”,分为两类:Map——存储“键值对”组成的对象;Collection——存储独立元素。Collection又可以分为List和Set两大块。List保持元素的顺序,可以有相同的元素,而Set不能有重复的元素。
首先对Set接口进行简要的说明。
存入Set的每个元素必须是惟一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set不保证维护元素的次序。Set与Collection有完全一样的接口。
在没有其他限制的情况下需要Set时应尽量使用HashSet,因为它对速度进行了优化。
下面是HashSet的定义:
public class HashSet<E>extends AbstractSet<E>implements Set<E>, Cloneable, java.io.Serializable
HashSet继承了AbstractSet,实现了Set接口。其实AbstractSet已经实现Set接口了。AbstractSet继承自AbstractCollection,而AbstractCollection实现了Collection接口的部分方法,而Set接口和Collection接口完全一致,所以AbstractSet只是实现了AbstractCollection没有实现的Set接口的方法和重写了部分AbstractCollection已经实现的方法。
下面是HashSet定义的属性:
//使用HashMap,利用key-value来存储Set中key private transient HashMap<E,Object> map; //定义一个默认的value为object对象 private static final Object PRESENT = new Object();
为什么会有一个HashMap
//构造方法一:定义一个默认的HashMap public HashSet() { map = new HashMap<>(); } // 构造方法二:利用给定的一个集合来初始化(HashMap中已经知道) //Math.max((int) (c.size()/.75f) + 1, 16)为:创建HashMap中哈希表的大小,默认最小值为16,装载因子为默认0.75 // 调用addAll方法将c中的元素添加到HashSet对象中 public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } //构造方法三:制定哈希表的容量和装载因子 public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } //构造方法四:指定哈希表的容量(默认装载因子0.75) public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } // 构造方法五:构造一个指定初始化容量和负载因子的LinkedHashMap //dummy参数被忽略,只是用于区分其他的,包含一个int、float参数的构造方法(方法重载) public HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
上面的构造方法都很简单,只有构造方法二中调用了addAll(Collection
//利用AbstractCollection中定义的方法 public boolean addAll(Collection<? extends E> c) { boolean modified = false; for (E e : c) // if (add(e)) modified = true; return modified; } //HashSet中重写的方法 public boolean add(E e) { //直接调用HashMap中的方法 return map.put(e, PRESENT)==null; }
看add(E e)方法只是调用了HashMap(构造方法中提供了创建LinkedHashMap的方式,但是LinkedHashMap是继承HashMap的,put方法也是调用HashMap的put方法)的put方法将e当做Key,PERSENT当做Value加入到map中并根据返回值判断是否添加成功。
因为HashMap的put方法在Key已经存在的情况下返回的是对应的Value值,若Key不存在则返回的是null,所以根据返回的是null可以确定新元素被添加到HashSet中了,如果返回的是其他值则说明Key已经存在,即元素已经在HashSet中已经存在,add(E e)返回的结果为false。虽然add(E e)返回false说明了HashSet添加元素失败,但实际上其中的map中的内容已经被替换,原先的值被PERSENT代替。
如果原先的值就是null呢?其实不用考虑这个问题,因为通过HashSet添加的元素,Value的内容都是PERSENT,不会出现null的情况。
//迭代器(key) public Iterator<E> iterator() { return map.keySet().iterator(); } //添加对象的个数 public int size() { return map.size(); } //判断是否为空 public boolean isEmpty() { return map.isEmpty(); } //是否包含对象key public boolean contains(Object o) { return map.containsKey(o); } //添加key public boolean add(E e) { return map.put(e, PRESENT)==null; } //删除key //remove(Object o)为什么还要判断结果呢?因为通过HashSet存入的元素,所对应的Value值都是PERSENT,如果传入的o不存在,map的remove方法返回为null,则对应的结果是HashSet的remove操作应该放回false,所以这里根据返回的结果判断是否移除成功。 public boolean remove(Object o) { //删除对象(key) return map.remove(o)==PRESENT; } //清空 public void clear() { map.clear(); }
LinkedHashSet源码分析
LinkedHashSet具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按照元素的插入次序显示。(LinkedHashMap中还可以按照LRU先后顺序显示),
看LinkedHashSet的内容。
//调用HashSet中的第五个构造函数 public LinkedHashSet(int initialCapacity, float loadFactor){ super(initialCapacity, loadFactor, true); } public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true); } public LinkedHashSet() { super(16, .75f, true); } public LinkedHashSet(Collection<? extends E> c) { super(Math.max(2*c.size(), 11), .75f, true); addAll(c); } }
LinkedHashSet继承自HashSet,HashSet基于HashMap实现,看LinkedHashSet类只是定义了四个构造方法,也没看到和链表相关的内容,为什么说LinkedHashSet内部使用链表维护元素的插入顺序(插入的顺序)呢?
//先看下HashSet的中的第五个构造函数
public HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
如果还有对LinkedHashMap不明白的,请看LinkedHashMap
相关文章推荐
- eclipse 下的ns3编译 不认中文路径名
- struts2 Action获取表单传值
- java_web连接SQL_server详细步骤
- Java8中使用流方式查询数据库的方法
- MAVEN+spring+springmvc+mybatis+mysql框架搭建+git管理
- Spark 报错: ERROR SparkUncaughtExceptionHandler rejected from java.util.concurrent.ThreadPoolExecutor
- java需要掌握内容、核心不断更新中
- Kmp算法的java实现
- java中的匿名内部类总结
- 第一章 Spring概述
- java中static作用详解
- hbase java api
- java多态实现与说明
- Mybatis与Spring整合事物
- SpringMVC+JPA+Hibernate配置
- Spring声明式事务管理与配置介绍
- jdk安装
- eclipse 安装svn插件
- Eclipse导入项目No projects are found to import
- struts2请求过程源码分析(转)