Set接口,LinkedHashSet,TreeSet

Set接口

1. 概述


Set作为Collection的子接口,没有添加额外的方法,它存储无序的不可重复的数据,在开发中使用比较少,后面会看Map的源码,会了Map,Set就会了。所以Set的重点是理解它的无序性和不可重复性。

为了保证无序性和不可重复性,我们要求添加到Set中的类要重写equals()和hashCode().重写的equals()和hashCode()要保证一致性:相等的对象(equals)必须具有相同的hash值(equals), 这样就可以使我们规定的相等的元素不会重复插入到Set中。

2. Set接口下常用的实现类


  1. hashSet: 作为Set接口的主要实现类;是一个线程不安全的;它可以存储 null。底层使用数组+链表实现的。

  2. LinkedHashSet: 是hashSet的子类;遍历内部数据时,可以按照添加的顺序去遍历。

  3. TreeSet: 可以按照添加元素指定的属性排序,所以要求放入同一个treeSet的对象必须是同一个类new出来的。

3. 添加元素的过程


以hashSet为例。当我们向hashSet中添加元素a时,首先先调用a元素所在类的hashCode()方法,计算出元素a的hash值,根据这个hash值,通过某种算法来确定该元素在hashSet底层数组中的一个存放位置。不同的hashCode通过这种算法可能会被放在同一个位置;如果这个位置上没有其他元素,则直接添加进去,若这个位置上有元素b(或以链表形式的多个元素),则比较a与该链表上所有元素的hash值;如果没有与a相等的hash值的元素,则添加进去,若有相同的hash值的元素,则调用a所在类的equals()方法;如果equals()返回true,则添加失败,返回False,则添加成功。

在jdk8中,添加到链表采用的是尾插法,而jdk7采用的是头插法。

var set = new HashSet();
     set.add(new Person("tom",12));
     set.add(new Person("tom",12));
     Iterator it = set.iterator();
     while(it.hasNext()){
         System.out.println(it.next());
     }

在没有重写Person类的hashCode()方法时,它调用的时Object的hashCode方法,这个方法是native,底层是C语言,我们不可见,它返回的是对象的存储地址。

4. LinkedHashSet


是hashSet的子类, 它也是无序,不可重复的。只不过它在添加数据时,还维护了两个变量,记录这个数据的前一个和后一个数据,使得它在遍历的时候能够按添加的顺序来遍历。

优点:对于频繁的遍历操作,LinkedHashSet效率高于hashSet。

5. TreeSet

5.1 概述


TreeSet的底层是红黑二叉树。TreeSet创造出来就是为了”按照添加元素指定的属性排序“这个功能,所以放入同一个treeSet的对象必须是同一个类new出来的。有两种排序的方式,自然排序(实现Comparable接口)和定制排序(Comparator)。

5.2 自然排序

如果我们使用自然排序的话,我们就要实现Comparable接口,也就是要实现compareTo()。 在自然排序中,比较两个对象相等的标准为compareTo()返回0,而不是equals()

下面是一个例子:

public class Person implements Comparable {
    private String name;
    private int age;

............

//按照name从大到小排序,age作为二级比较,从小到大排  
@Override  
public int compareTo(Object o) {  
    if (o instanceof Person){  
        Person person = (Person)o;  
 int compare =  -this.name.compareTo(person.name);  
 if(compare != 0){  
            return compare;  
 }  
        else {  
           return Integer.compare(this.age, person.age);  
 }  
    }  
    else{  
        throw new RuntimeException("输入类型不匹配");  
 }  

}
}

5.3 定制排序

我们创建一个Comparator对象,在这个对象里重写compare()方法,之后在声明TreeSet的时候,把这个对象作为参数传入。

在定制排序中,比较两个对象相等的标准为compare返回0,而不是equals()。

Comparator com = new Comparator() {  
    //只考虑年龄,按小到大排序  
 @Override  
 public int compare(Object o1, Object o2) {  
        if(o1 instanceof Person && o2 instanceof Person){  
            Person p1 = (Person) o1;  
 Person p2 = (Person) o2;  
 return Integer.compare(p1.getAge(),p2.getAge());  
 }else {  
            throw new RuntimeException("输入的数据类型匹配");  
 }  
    }  

};  

TreeSet set = new TreeSet(com);
set.add(new Person("luca",20));
set.add(new Person("lucb",20));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值