Set及使用场景
类型 | 特点 |
---|---|
Set(interface) | 存入Set的每个元素都必须是唯一的。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。 |
HashSet* | 为快速查找而设计的Set。存入HashSet的元素必须定义hashCode() |
TreeSet | 保持次序的Set,底层为树结构。使用它可以从Set中提取有序的序列。元素必须实现Comparable接口 |
LinkedHashSet | 具有HashSet 的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按插入的次序显示。元素也必须定义hashCode()方法。 |
使用场景:
如果没有任何限制,则建议使用HashSet,因为它对速度进行了优化;
如果要保证插入的元素有序,则使用TreeSet;
如果需要以插入的顺序读取元素,则使用LinkedHashSet。
底层源码剖析
HashSet底层
private static final Object PRESENT = new Object();
//构造方法
public HashSet() {
map = new HashMap<>();
}
//插入
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//删除
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
分析:
HashSet底层实质上是HashMap,插入的时候将数据存储到HashMap的key中,并将HashMap的value存储为一个名为PRESENT的object常量。
TreeSet
public TreeSet() {
this(new TreeMap<E,Object>());
}
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
分析:
TreeMap底层也是使用Map来实现,它使用的是TreeMap。
LinkedHashSet
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
//构造方法
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
public LinkedHashSet() {
super(16, .75f, true);
}
}
可以看出,LinkedHashSet底层构造方法中调用了LinkedHashSet的有参构造方法,而在HashSet中该构造方法调用了LinkedHashMap,实如下。
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
使用方式
代码来自Java编程思想。
package javabasic.settest;
import huawei.Test;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
/**
* @author wq
*/
//如果要使用Set,必须要重写equals()方法
class SetType{
int i;
public SetType(int n){
i=n ;
}
public boolean equals(Object o){
return o instanceof SetType && (i==((SetType)o).i);
}
@Override
public String toString() {
return Integer.toString(i);
}
}
//如果想要存入HashSet和HashMap,必须重写hashCode()方法
class HashType extends SetType{
public HashType(int n) {
super(n);
}
public int hashCode(){
return i;
}
}
//如果将对象存入TreeSet中,必须重写compareTo()方法
class Treetype extends SetType implements Comparable<Treetype>{
public Treetype(int n) {
super(n);
}
@Override
public int compareTo(Treetype o) {
return o.i<i?-1:(o.i==i?0:1);
}
}
public class TypesForSets {
static <T> Set<T> fill(Set<T> set,Class<T> type){
try{
for(int i=0;i<10;i++){
set.add(type.getConstructor(int.class).newInstance(i));
}
}catch (Exception e){
throw new RuntimeException(e);
}
return set;
}
static <T> void test(Set<T> set,Class<T> type){
fill(set,type);
fill(set,type);
fill(set,type);
System.out.println(set);
}
public static void main(String[] args) {
test(new HashSet<HashType>(),HashType.class);
test(new LinkedHashSet<HashType>(),HashType.class);
test(new TreeSet<Treetype>(),Treetype.class);
new LinkedHashSet<>().iterator();
test(new HashSet<SetType>(),SetType.class);
test(new HashSet<Treetype>(),Treetype.class);
test(new LinkedHashSet<SetType>(),SetType.class);
test(new LinkedHashSet<Treetype>(),Treetype.class);
try {
test(new TreeSet<SetType>(),SetType.class);
}catch (Exception e){
System.out.println(e.getMessage());
}
try{
test(new TreeSet<HashType>(),HashType.class);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
小结:
- Set保证存入的元素必须唯一,因此必须重写equals()方法。
- 数据要存入HashMap和LinkedHashMap必须重写hashCode()方法。
- 数据存入TreeSet必须重写compare方法。
TODO:
- equals()和hashCode()方法;
- TreeMap的底层实现——红黑树;