java里集合的set是一种无序,不能重复的集合,Set直接继承了Collection,是一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象
HashSet的介绍
HashSet 是一个没有重复元素的集合。
它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。
HashSet是非同步的。可以通过
Set s = Collections.synchronizedSet(new HashSet(...));
来实现同步。
此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果对 set 进行修改,除非通过迭代器自身的 remove 方法,否则在任何时间以任何方式对其进行修改,Iterator 都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来在某个不确定时间发生任意不确定行为的风险。
HashSet的构造器:
// 默认构造函数
public HashSet() {
//调用hashMap的构造函数,生成一个map
map = new HashMap<>();
}
// 带集合的构造函数
public HashSet(Collection<? extends E> c) {
//调用hashMap的构造器,利用Math的max方法得到c.size()/.75f) + 1 与 16的较大值,在hashMap里,从效率(时间成本和空间成本)来考虑,HashMap的加载因子是0.75,
//并且当HashMap的“阈值”(阈值=HashMap总的大小*加载因子) < “HashMap实际大小”时,就需要将HashMap的容量翻倍
//所以,(c.size()/.75f) + 1 计算出来的正好是总的空间大小。
// HashMap的构造函数中也会重新计算,找出比“指定大小”大的最小的2的指数倍的数。
// 所以,这里指定为16是从性能考虑。避免重复计算。
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
// 将集合中的全部元素都添加到HashSet中
addAll(c);
}
// 指定HashSet初始容量和加载因子的构造函数
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
// 指定HashSet初始容量的构造函数
public HashSet(int initialCapacity)
// 指定HashSet初始容量和加载因子的构造函数,dummy没有任何作用
HashSet(int initialCapacity, float loadFactor, boolean dummy){
map = new HashMap<>(initialCapacity);
}
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
HashSet的数据结构
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable { }
通过源码可以看到直接继承了AbstractSet,并且实现了Set接口。HashSet的本质是一个”没有重复元素”的集合,它是通过HashMap实现的。HashSet中含有一个”HashMap类型的成员变量”map,HashSet的操作函数,实际上都是通过map实现的。
private transient HashMap<E,Object> map;
//PRESENT是向map中插入key-value对应的value,hashSet只需要map的键,所以对每个键都插入一个值,值固定都是PRESENT
private static final Object PRESENT = new Object();
这里可以更加了解hashSet是hashMap实现的,它的方法也都是靠hashMap来实现的。
遍历Set方法:
for(String s:set){
System.out.println(s);
}
//使用iterator迭代器
for(Iterator iter = set.iterator();iter.hasNext();){
String s = (String)iter.next();
System.out.println(s);
}
TreeSet的介绍
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
TreeSet继承了AbstractSet,实现来NavigableSet,Cloneable,Serializable接口,AbstractSet提供 Set 接口的基本实现,NavigableSet是继承了 SortedSet的,具有了为给定搜索目标报告最接近匹配项的导航方法。方法 lower、floor、ceiling 和 higher 分别返回小于、小于等于、大于等于、大于给定元素的元素,如果不存在这样的元素,则返回 null。Cloneable支持克隆,Serializable支持序列化。
//底层使用NavigableMap来保存TreeSet的元素
private transient NavigableMap<E,Object> m;
///PRESENT会被当做Map的value与key构建成键值对
private static final Object PRESENT = new Object();
构造方法:
//使用指定的navigable map来构造TreeSet
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
//默认构造方法,底层使用TreeMap来存储TreeSet元素
public TreeSet() {
this(new TreeMap<E,Object>());
}
// 使用指定的构造器,构造一个TreeMap来保存TreeSet的数据
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<E,Object>(comparator));
}
// 构造一个指定Collection参数的TreeSet
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
// 构造一个指定SortedMap的TreeSet,根据SortedMap的比较器来来维持TreeSet的顺序
public TreeSet(SortedSet<E> s) {
this(s.comparator());
addAll(s);
}
看到了它的实现是靠NavigableMap,那么NavigableMap是什么呢?点进去发现这是个接口,有很多方法
public interface NavigableMap<K,V> extends SortedMap<K,V> {
// 获取小于指定key的第一个节点对象
Map.Entry<K,V> lowerEntry(K key);
// 获取小于指定key的第一个key
K lowerKey(K key);
// 获取小于或等于指定key的第一个节点对象
Map.Entry<K,V> floorEntry(K key);
// 获取小于或等于指定key的第一个key
K floorKey(K key);
// 获取大于或等于指定key的第一个节点对象
Map.Entry<K,V> ceilingEntry(K key);
// 获取大于或等于指定key的第一个key
K ceilingKey(K key);
// 获取大于指定key的第一个节点对象
Map.Entry<K,V> higherEntry(K key);
// 获取大于指定key的第一个key
K higherKey(K key);
// 获取Map的第一个(最小的)节点对象
Map.Entry<K,V> firstEntry();
// 获取Map的最后一个(最大的)节点对象
Map.Entry<K,V> lastEntry();
// 获取Map的第一个节点对象,并从Map中移除改节点
Map.Entry<K,V> pollFirstEntry();
// 获取Map的最后一个节点对象,并从Map中移除改节点
Map.Entry<K,V> pollLastEntry();
// 返回当前Map的逆序Map集合
NavigableMap<K,V> descendingMap();
// 返回当前Map中包含的所有key的Set集合
NavigableSet<K> navigableKeySet();
// 返回当前map的逆序Set集合,Set由key组成
NavigableSet<K> descendingKeySet();
// 返回当前map中介于fromKey(fromInclusive是否包含)和toKey(toInclusive是否包含) 之间的子map
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive);
// 返回介于map第一个元素到toKey(inInclusive是否包含)之间的子map
NavigableMap<K,V> headMap(K toKey, boolean inclusive);
// 返回当前map中介于fromKey(inInclusive是否包含) 到map最后一个元素之间的子map
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
// 返回当前map中介于fromKey(包含)和toKey(不包含)之间的子map
SortedMap<K,V> subMap(K fromKey, K toKey);
// 返回介于map第一个元素到toKey(不包含)之间的子map
SortedMap<K,V> headMap(K toKey);
// 返回当前map中介于fromKey(包含) 到map最后一个元素之间的子map
SortedMap<K,V> tailMap(K fromKey);
}
NavigableMap继承了SortedMap接口,而SortedMap继承了Map接口,所以NavigableMap是在Map接口的基础上丰富了这些对于边界查询的方法,但是不妨碍你只是用其中Map中自身的功能。