剖析HashSet和TreeSet


Set注重独一无二的性质,该体系集合可以知道某物是否已近存在于集合中,不会存储重复的元素,用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复

HashSet

HashSet的底层实现

在这里插入图片描述
HashSet的底层是HashMap,HashSet其实就是在操作HashMap的key,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。
1.因为HashMap是无序的,因此HashSet也不能保证元素的顺序
2.因为HashSet中没有对应同步的操作,因此是线程不安全的
3.支持null元素(因为hashMap也支持null键和null值)

存储规则

HashSet存储元素的顺序并不是按照存入时的顺序是按照哈希值来存的所以取数据也是按照哈希值取,存储元素不能有重复的元素,可以包含一个null

如何判定两个元素重复

HashSet不存入重复元素的规则.使用hashcode和equals
1.当你试图把对象加入HashSet时,HashSet会使用对象的hashCode来判断对象加入的位置。同时也会与其他已经加入的对象的hashCode进行比较,如果没有相等的hashCode,HashSet就会假设对象没有重复出现。
2.如果元素(对象)的hashCode值相同,会继续使用equals 进行比较.如果 equals为true 那么HashSet认为新加入的对象重复了,所以加入失败。如果equals 为false那么HashSet 认为新加入的对象没有重复.新元素可以存入

总结一下

特点:无序,不重复
底层存储:hashMap,操作的时间上时hashMap的key,其value的值固定为PRESENT
判断两个元素重复的依据:hashcode和equals
两个元素hash值冲突时存储方式:与hashMap一样,转链表加红黑树

treeSet

treeSet的底层实现

TreeSet底层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap,通过key来存储Set的元素, 而value是一个统一的object对象,TreeMap是红黑二叉树的典型实现
1.TreeMap 中的所有 Entry 总是按 key 根据指定排序规则保持有序状态,TreeSet 中所有元素总是根据指定排序规则保持有序状态
2.红黑树是一种自平衡排序二叉树,树中每个节点的值,都大于或等于在它的左子树中的所有节点的值,并且小于或等于在它的右子树中的所有节点的值,这确保红黑树运行时可以快速地在树中查找和定位的所需节点。
(和树相关的,例如数据B+Tree,hashMap解决hash冲突转换的红黑树,treeSet,treeMap等,和树带上点关系的一般都会考虑到有序和查找快两个方面,但同时会有插入效率影响,树的节点的左旋右旋等,所以涉及到这样的操作,一般都是查询快,增删影响大,适合用于存储数量大,并且插入删除操作较少的数据)

存储规则

public class TreeSet<E> extends AbstractSet<E> 
    implements NavigableSet<E>, Cloneable, java.io.Serializable 
 { 
    // 使用 NavigableMap 的 key 来保存 Set 集合的元素
    private transient NavigableMap<E,Object> m; 
    // 使用一个 PRESENT 作为 Map 集合的所有 value。
    private static final Object PRESENT = new Object(); 
    // 包访问权限的构造器,以指定的 NavigableMap 对象创建 Set 集合
    TreeSet(NavigableMap<E,Object> m) 
    { 
        this.m = m; 
    } 
public TreeSet()                                      
{ 
        // 以自然排序方式创建一个新的 TreeMap,
        // 根据该 TreeSet 创建一个 TreeSet,
        // 使用该 TreeMap 的 key 来保存 Set 集合的元素
        this(new TreeMap<E,Object>()); 
    } 
public TreeSet(Comparator<? super E> comparator)       
{ 
        // 以定制排序方式创建一个新的 TreeMap,
        // 根据该 TreeSet 创建一个 TreeSet,
        // 使用该 TreeMap 的 key 来保存 Set 集合的元素
        this(new TreeMap<E,Object>(comparator)); 
    } 
    public TreeSet(Collection<? extends E> c) 
    { 
        // 调用①号构造器创建一个 TreeSet,底层以 TreeMap 保存集合元素
        this(); 
        // 向 TreeSet 中添加 Collection 集合 c 里的所有元素
        addAll(c); 
    } 
    public TreeSet(SortedSet<E> s) 
    { 
        // 调用②号构造器创建一个 TreeSet,底层以 TreeMap 保存集合元素
        this(s.comparator()); 
        // 向 TreeSet 中添加 SortedSet 集合 s 里的所有元素
        addAll(s); 
    } 
    //TreeSet 的其他方法都只是直接调用 TreeMap 的方法来提供实现
    ... 
    public boolean addAll(Collection<? extends E> c) 
    { 
        if (m.size() == 0 && c.size() > 0 && 
            c instanceof SortedSet && 
            m instanceof TreeMap) 
        { 
            // 把 c 集合强制转换为 SortedSet 集合
            SortedSet<? extends E> set = (SortedSet<? extends E>) c; 
            // 把 m 集合强制转换为 TreeMap 集合
            TreeMap<E,Object> map = (TreeMap<E, Object>) m; 
            Comparator<? super E> cc = (Comparator<? super E>) set.comparator(); 
            Comparator<? super E> mc = map.comparator(); 
            // 如果 cc 和 mc 两个 Comparator 相等
            if (cc == mc || (cc != null && cc.equals(mc))) 
            { 
                // 把 Collection 中所有元素添加成 TreeMap 集合的 key 
                map.addAllForTreeSet(set, PRESENT); 
                return true; 
            } 
        } 
        // 直接调用父类的 addAll() 方法来实现
        return super.addAll(c); 
} 
 } 

如何实现有序

  • 对于基本的数据类型。比如Integer或者是String ,在你把数据添加进去之后,TreeSet会按照这个类的compareTo方法进行排序,这时候不需要大家进行额外的工作,只是把数据存进去就好了
  • 如果存入的是一些没有实现compareTo方法的类,比如你自己定义的类,那么你需要在学生类中implements Comparator接口中的方法compare 或者 implements Comparable接口 然后重写其中的compareTo方法 ,指定出你想要的排序规则
  • 在对TreeSet进行实例化的时候,在构造函数中 通过匿名内部类 重写其中的compare方法,在其中指定出具体的排序方法,在方法二中指定的顺序为先按照年龄排序,年龄一样时再按照学号进行排序

总结一下

特点:有序,不重复,tree=有序,set=不重复,map=键值对,hash=哈希,按照这个来准没错
底层存储:treeMap,操作的是treeMap的key,其value的值固定为PRESENT
判断两个元素重复的依据:

  1. Comparable
    它里面的元素需要实现comparable接口才具备可比性。然后想具体拿什么属性来判断是否相同就需要覆盖compareto()方法,如果comparabto()返回的是0则表示两个元素相等。它同时会对元素按一定的次序进行排序
class student implements Comparable
{
	public int compareTo(Object obj)
	{
		if(!(obj instanceof student))
			throw new RuntimeException("不是学生对象");
		student s=(student)obj;
		if(this.age>s.age)
			return 1;
		if(this.age==s.age)
			return this.name.compareTo(s.name);   //这是String的方法,用于判断两个字符串的大小
		return -1;
	}
  1. Comparator
    如果元素不具备比较性或者所采用的比较方式不是想要的,那么就可以采用这种方式。
class mycompare implements Comparator
{
	public int compare(Object c1,Object c2)
	{
		student s1=(student)c1;
		student s2=(student)c2;
		int num=s1.getname().compareTo(s2.getname());
		if(num==0)
			return s1.getage()-s2.getage(); 
		return num; 
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值