HashSet 和TreeSet
HashSet 特点:
- 不能保证元素的顺序,元素的顺序可能会发生变化
- 不能同步的,线程不安全的
- 集合元素可以是Null,但是只能有一个Null
HashSet 简易原理:
HashSet是底层是由HashMap组成的当向HashSet插入一个元素的时候,首先调用该对象的HashCode()方法获取HashCode,在根据HashCode判断该对象应该储存在那个位置
HashSet中无法储存相同的对象,如果HashCode相同在调用equals方法判断是否属于同一个对象,所以存入HashSet的对象都必须实现HashCode()和equals方法
HashSet 部分源码:
- 核心属性
//数据都存放在这个Map当中
private transient HashMap<E,Object> map;
//Map 中所有的Key都是一个Object
private static final Object PRESENT = new Object();
- 4个构造函数
public HashSet() {
//创建一个Map Map默认长度为16
//由于HashSet底层是HashMap 所以扩容机制如同HashMap
//当元素超过 当前容量*负载因子(0.75f)的时候就会进行扩容
// 每一次扩容是当前容量的1倍
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
//比较让如的集合和默认长度16哪一个大
//如果放入的长度小于16则使用默认长度,并且吧所有对象addAll
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) {
//可填写默认容量 负载因子使用默认的0.75f
map = new HashMap<>(initialCapacity);
}
public int size() {
//大部分操作直接调用map
return map.size();
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean add(E e) {
// add操作的时候 首先调用该对象的HashCode()方法获取HashCode,在根据HashCode 判断该对象应该储存在那个位置HashSet中无法储存相同的对象,如果HashCode相同在调用equals方法判断是否属于同一个对象
// 计算出来的对象 当做Map的Key,Value都是统一的PRESENT (new Object())
return map.put(e, PRESENT)==null;
}
TreeSet 特点:
- 能够保证元素的顺序,TreeSet 支持二中排序方式,自然排序和定制排序,默认使用自然排序,定制排序需要在构造函数当中传入一个比较器
- 不允许存放Null,如果存放Null则直接NullPointerException
- 数据不同步的,线程不安全的
TreeSet简易理解:
TreeSet 是基于TreeMap实现的,TreeMap 是一个具有自然排序依据定制排序的map,能够保证数据的顺序排序,TreeSet 使用二叉平衡树来储存数据,由于要顺序排序所以性能相对来说比较差
TreeSet 部分源码:
// 数据都储存在这NavigableMap当中 TreeMap 实现了NavigableMap
private transient NavigableMap<E,Object> m;
// 和HashSet 一样 TreeSet中的Map value 都储存的是一个Object
private static final Object PRESENT = new Object();
//无参构造函数 直接创建一个New TreeMap();
// TreeMap 底层是由红黑树维护的所以不存在容量 或者扩容机制
public TreeSet() {
this(new TreeMap<E,Object>());
}
// 放入一个定制比较器进行构造
public TreeSet(Comparator<? super K> comparator) {
this.comparator = comparator;
}
// 放入一个集合 然后调用addAll
public TreeSet(Collection<? extends E> c) {
this();
addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
// 调用全局的 map 判断size 是否有数据
// 并且 传入类型是否是SortedSet 或者TreeMap 如果不是
// 调用父类AbstractCollection 的addAll
if (m.size()==0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap) {
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<?> cc = set.comparator();
Comparator<? super E> mc = map.comparator();
if (cc==mc || (cc != null && cc.equals(mc))) {
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
//AbstractCollection 中的addAll 直接迭代所有的kev 并切入到set当中
return super.addAll(c);
}
//add 操作 直接 将 输入的值当做Map的key
//PRESENT (new Object()) 作为 value存入Map
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
// 大部分操作 直接调用TreeMap中的方法 (contains size isEmpty)
........
HashSet 和TreeSet 大部分特点相同, Treeset可以保持元素的顺序,由于需要保持元素顺序,所以性能略差,HashSet的性能要好得多,是因为HashSet的底层是有HashMap储存的访问速度非常快,如果不需要保持元素属性并且不要求重复可以使用HashSet,如果需要元素顺序则使用TreeSet
1,treeSet去重原理:compareTo
可以实现排序及去重:如果compareTo返回0,说明是重复的,返回的是自己的某个属性和另一个对象的某个属性的差值,如果是负数,则往前面排,如果是正数,往后面排;
应用:类实现compareable接口,覆写其compareto方法,根据自己的需要改变其排序及去重规则,比如person类,根据其年龄进行去重和排序
2,hashSet去重原理:1,hashCode 2,equals是否相同
两个方法可以快速生成,hashCode是几个属性的hashCode共同计算的结果
(基本数据类型的实现类生成的hashCode是其拆箱后的值的比较,跟引用数据类型的规则不同,如Integer类)