java集合类源码分析之Set详解
set集合与list一样,都是继承自collection接口,常用的实现类有hashset和treeset。值得注意的是,hashset是通过hashmap来实现的而treeset是通过treemap来实现的,所以hashset和treeset都没有自己的数据结构,具体可以归纳如下:
•set集合中的元素不能重复,即元素唯一
•hashset按元素的哈希值存储,所以是无序的,并且最多允许一个null对象
•treeset按元素的大小存储,所以是有序的,并且不允许null对象
•set集合没有get方法,所以只能通过迭代器(iterator)来遍历元素,不能随机访问
1.hashset
下面给出hashset的部分源码,以理解它的实现方式。
static final long serialversionuid = -5024744406713321676l; private transient hashmap<e,object> map; // dummy value to associate with an object in the backing map private static final object present = new object();
观察源码,我们知道hashset的数据是存储在hashmap的实例对象map中的,并且对应于map中的key,而object类型的引用present则是对应于map中的value的一个虚拟值,没有实际意义。联想到hashmap的一些特性:无序存储、key值唯一等等,我们就可以很自然地理解set集合元素不能重复以及hashset无序存储的特性了。
下面从源代码的角度来理解hashset的基本用法:
•构造器(四种)
1.hashset() 空的构造器,初始化一个空的hashmap
2.hashset(collection<? extends e> c) 传入一个子集c,用于初始化hashmap
3.hashset(int initialcapacity, float loadfactor) 初始化一个空的hashmap,并指定初始容量和加载因子
4.hashset(int initialcapacity) 初始化一个空的hashmap,并指定初始容量
public hashset() { map = new hashmap<>(); } 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); } public hashset(int initialcapacity) { map = new hashmap<>(initialcapacity); }
•插入元素
1.add(e e) 插入指定元素(调用hashmap的put方法实现)
set<string> hashset = new hashset<string>(); hashset.add("d"); hashset.add("b"); hashset.add("c"); hashset.add("a");
•查找元素
1.contains(object o) 判断集合中是否包含指定的元素(调用hashmap的containskey方法实现)
public boolean contains(object o) { return map.containskey(o); }
2.由于hashset的实现类中没有get方法,所以只能通过迭代器依次遍历,而不能随机访问(调用hashmap中keyset的迭代器实现)
public iterator<e> iterator() { return map.keyset().iterator(); }
应用示例:
set<string> hashset = new hashset<string>(); hashset.add("d"); hashset.add("b"); hashset.add("c"); hashset.add("a"); for (iterator iterator = hashset.iterator(); iterator.hasnext();) { string string = (string) iterator.next(); system.out.print(string+" "); }//d a b c
•修改元素
由于hashmap中的key值不能修改,所以hashset不能进行修改元素的操作
•删除元素
1.remove(object o) 删除指定元素(调用hashmap中的remove方法实现,返回值为true或者false)
public boolean remove(object o) { return map.remove(o)==present; }
2.clear() 清空元素(调用hashmap中的clear方法实现,没有返回值)
public void clear() { map.clear(); }
2.treeset
treeset是sortedset接口的唯一实现类。前面说过,treeset没有自己的数据结构而是通过treemap实现的,所以treeset也是基于红黑二叉树的一种存储结构,所以treeset不允许null对象,并且是有序存储的(默认升序)。
private transient navigablemap<e,object> m; // dummy value to associate with an object in the backing map private static final object present = new object();
上述源代码中的navigablemap是继承自srotedmap的一个接口,其实现类为treemap,因此treeset中的数据是通过treemap来存储的,此处的present也是没有实际意义的虚拟值。
下面从源代码的角度来理解hashset的基本用法:
•构造器(四种)
1.treeset() 空的构造器,初始化一个空的treemap,默认升序排列
2.treeset(comparator<? super e> comparator) 传入一个自定义的比较器,常常用于实现降序排列
3.treeset(collection<? extends e> c) 传入一个子集c,用于初始化treemap对象,默认升序
4.treeset(sortedset<e> s) 传入一个有序的子集s,用于初始化treemap对象,采用子集的比较器
public treeset() { this(new treemap<e,object>()); } public treeset(comparator<? super e> comparator) { this(new treemap<>(comparator)); } public treeset(collection<? extends e> c) { this(); addall(c); } public treeset(sortedset<e> s) { this(s.comparator()); addall(s); }
应用示例
//自定义一个比较器,实现降序排列 set<integer> treeset = new treeset<integer>(new comparator<integer>() { @override public int compare(integer o1, integer o2) { // return 0; //默认升序 return o2.compareto(o1);//降序 } }); treeset.add(200); treeset.add(120); treeset.add(150); treeset.add(110); for (iterator iterator = treeset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); } //200 150 120 110
arraylist<integer> list = new arraylist<integer>(); list.add(300); list.add(120); list.add(100); list.add(150); system.out.println(list); //[300, 120, 100, 150] //传入一个子集,默认升序排列 treeset<integer> treeset = new treeset<integer>(list); for (iterator iterator = treeset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); }//100 120 150 300
/* * 传入一个有序的子集,采用子集的比较器 * 注意子集的类型必须是sortedset及其子类或者实现类,否则将采用默认的比较器 * 所以此处subset的类型也可以是treeset。 */ sortedset<integer> subset = new treeset<integer>(new comparator<integer>() { @override public int compare(integer o1, integer o2) { // return 0; //默认升序 return o2.compareto(o1);//降序 } }); subset.add(200); subset.add(120); subset.add(150); subset.add(110); for (iterator iterator = subset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); } //200 150 120 110 system.out.println(); set<integer> treeset = new treeset<integer>(subset); for (iterator iterator = treeset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); }//200 150 120 110 system.out.println(); treeset.add(500); treeset.add(100); treeset.add(105); for (iterator iterator = treeset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); }//500 200 150 120 110 105 100
• 插入元素
1.add(e e) 插入指定的元素(调用treemap的put方法实现)
2.addall(collection<? extends e> c) 插入一个子集c
arraylist<integer> list = new arraylist<integer>(); list.add(300); list.add(120); list.add(100); list.add(150); system.out.println(list); //[300, 120, 100, 150] set<integer> treeset = new treeset<integer>(); //插入一个子集,默认升序 treeset.addall(list); for (iterator iterator = treeset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); }//100 120 150 300
•查找元素
1.contains(object o) 判断集合中是否包含指定对象(调用treemap的containskey方法实现)
2.与hashset一样,treeset的实现类中没有get方法,所以只能通过迭代器依次遍历,而不能随机访问(调用treemap中keyset的迭代器实现)。
•修改元素
treeset不能进行修改元素的操作,原因与hashset一样。
•删除元素
1.remove(object o) 删除指定元素(调用treemap中的remove方法实现,返回true或者false)
public boolean remove(object o) { return m.remove(o)==present; }
2.clear() 清空元素(调用treemap中的clear方法实现,无返回值)
public void clear() { m.clear(); }
应用示例:
arraylist<integer> list = new arraylist<integer>(); list.add(300); list.add(120); list.add(100); list.add(150); system.out.println(list); //[300, 120, 100, 150] set<integer> treeset = new treeset<integer>(); //插入一个子集,默认升序 treeset.addall(list); for (iterator iterator = treeset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); }//100 120 150 300 system.out.println(treeset.remove(100));//true for (iterator iterator = treeset.iterator(); iterator.hasnext();) { integer integer = (integer) iterator.next(); system.out.print(integer+" "); }//120 150 300 treeset.clear(); system.out.println(treeset.size());//0
至此,hashset和treeset的存储结构及基本用法介绍完毕。
以上这篇java集合类源码分析之set详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
推荐阅读
-
基于Java中最常用的集合类框架之HashMap(详解)
-
练习-Java集合类之Set的TreeSet之自定义排序规则
-
死磕 java集合之ArrayList源码分析
-
死磕 java集合之LinkedHashMap源码分析
-
Java基础之Collections框架Map接口实现类HashMap及其源码分析(1)
-
Java集合类List、Set如何使用详解
-
死磕 java集合之TreeMap源码分析(二)- 内含红黑树分析全过程
-
死磕 java集合之TreeMap源码分析(三)- 内含红黑树分析全过程
-
死磕 java集合之ConcurrentHashMap源码分析(一)
-
Java容器类源码分析之Iterator与ListIterator迭代器(基于JDK8)