HashSet/LinkedHashSet/TreeSet
Collection 系列文章的总目录:
- Collection 体系的三个核心约定
- Sorted & Navigable
- Iterator & Iterable
- Java 中的数组
- ArrayList
- LinkedList
- HashMap
- LinkedHashMap
- TreeMap
- HashSet/LinkedHashSet/TreeSet
HashSet
HashSet
内部是直接使用HashMap
来实现的是一种变体的
装饰器模式
实现原理:
添加元素:
add() :
- 把
元素
放到 HashMap 的key
,value
放入一个常量PRESENT
删除元素:
remove():
- 从 HashMap 中 remove,并判断
value
是否等于PRESENT
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
问题:
Q:突然发现,很多集合的方法,入参都是使用 Object,而不是泛型,这是为什么?
- 集合内部依赖 Object.equals() 方法,而 Object.equals() 方法接收的参数类型是 Object 类型。Object子类如果重写了equals()方法的话,并没有要求参数类型和当前类类型一定要相同才返回true。
- 使用类似 Set<? extends Foo> set 这样的变量,除非传 null,否则编译器都会报错。因为 ? extends Foo 不是一个确定的类型,任何确定的类型,即使是Foo的子类,该方法都不能接收,因为编译期不能确定这个Foo的子类就一定是创建set时指定的那个类型。
Q:那为什么 add() 的时候需要使用泛型?
- 要保证集合内保存的元素类型
- 那些使用 Object 类型的方法,都是使用外部传入的对象去和集合内的元素做比较,而这并不强制要求传入的对象类型要求一样,只需要能和集合中的元素进行比较即可。
参考:为什么Map.containsKey()方法的参数类型是Object?
LinkedHashSet
LinkedHashSet 直接继承 HashSet
实现原理:
- HashSet提供了一个构造,可以指定使用LinkedHashMap 作为实现
- 所以 LinkedHashSet 只需要调用这个构造即可
// dummy只是用来区别不同的构造,本身并没用
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
TreeSet:
TreeSet 也是直接使用 TreeMap
实现原理:
- 和 HashSet 类似,就不再赘述了
- 内部持有的是 NavigableMap 类型,就不是 TreeMap 类型
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}