【Java集合】Set 接口 —— HashSet 与 TreeSet 详解

Set接口和List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格。与List接口不同的是,Set接口中的元素无序,并且都会以某种规则保证存入的元素不出现重复。

Set接口主要有两个实现类,分别是HashSet和TreeSet。其中,HashSet是根据对象的哈希值来确定元素在集合中的存储的位置,因此具有良好的存取和查找性能;TreeSet则是以二叉树的方式来存储元素,它可以实现对集合中的元素进行排序。

HashSet集合

HashSet是Set接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的。当向HashSet集合中添加一个元素时,首先会调用该元素的hashCode()方法来确定元素的存储位置,然后再调用元素对象的equals()方法来确保该位置没有重复元素。Set集合与List集合存取元素的方式都一样。

当调用HashSet集合的add()方法存入元素时,首先调用当前存入元素的hashCode()方法获得对象的哈希值,然后根据对象的哈希值计算出一个存储位置。如果该位置上没有元素,则直接将元素存入,如果该位置上有元素存在,则会调用equals()方法让当前存入的元素依次和该位置上的元素进行比较,如果返回的结果为false就将该元素存入集合,返回的结果为true则说明有重复元素,就将该元素舍弃。整个存储的流程如图2所示。

class Student {
    private String id;
    private String name;
    public Student(String id, String name) { 
        this.id = id;
        this.name = name;
    }
      // 重写toString()方法
    public String toString() { 
        return id + ":" + name;
    }
      // 重写hashCode()方法
    public int hashCode() { 
        return id.hashCode();    // 返回id属性的哈希值
    }
      // 重写equals()方法
    public boolean equals(Object obj) { 
        if (this == obj) {     // 判断是否是同一个对象
            return true;       // 如果是,直接返回true
        }
        if (!(obj instanceof Student)) {     // 判断对象是为Student类型
            return false;       // 如果对象不是Student类型,返回false
        }
        Student stu = (Student) obj;          // 将对象强转为Student类型
         boolean b = this.id.equals(stu.id); // 判断id值是否相同
        return    b;                                 // 返回判断结果
    }

Student类重写了Object类的hashCode()和equals()方法。在hashCode()方法中返回id属性的哈希值,在equals()方法中比较对象的id是否相等,并返回结果。当调用HashSet集合的add()方法添加stu3对象时,发现它的哈希值与stu2对象相同,而且stu2.equals(stu3)返回true,HashSet集合认为两个对象相同,因此重复的Student对象被舍弃了。

TreeSet集合

TreeSet是Set接口的另一个实现类,它内部采用平衡二叉树来存储元素,这样的结构可以保证TreeSet集合中没有重复的元素,并且可以对元素进行排序。

图1 二叉树的存储结构

针对TreeSet集合存储元素的特殊性,TreeSet在继承Set接口的基础上实现了一些特有的方法,如表1所示。

表1 TreeSet集合的特有方法

方法声明

功能描述

Object first()

返回TreeSet集合的首个元素

Object last()

返回TreeSet集合的最后一个元素

Object lower(Object o)

返回TreeSet集合中小于给定元素的最大元素,如果没有返回null

Object floor(Object o)

返回TreeSet集合中小于或等于给定元素的最大元素,如果没有返回null

Object higher(Object o)

返回TreeSet集合中大于给定元素的最小元素,如果没有返回null

Object ceiling(Object o)

返回TreeSet集合中大于或等于给定元素的最小元素,如果没有返回null

Object pollFirst()

移除并返回集合的第一个元素

Object pollLast()

移除并返回集合的最后一个元素

集合中的元素在进行比较时,都会调用compareTo()方法,该方法是Comparable接口中定义的,因此要想对集合中的元素进行排序,就必须实现Comparable接口。Java中大部分的类都实现了Comparable接口,并默认实现了接口中的CompareTo()方法,如Integer、Double和String等。

为了解决这个问题,Java提供了两种TreeSet的排序规则,分别为:自然排序和定制排序。在默认情况下,TreeSet集合都是采用自然排序。

1.自然排序

自然排序要求向TreeSet集合中存储的元素所在类必须实现Comparable接口,并重写compareTo()方法,然后TreeSet集合就会对该类型元素使用compareTo()方法进行比较,并默认进行升序排序。

       public int compareTo(Object obj){
           Teacher s = (Teacher) obj;    
             // 定义比较方式,先比较年龄age,再比较名称name     
            if(this.age -s.age > 0) {        
                    return 1;
            }
            if(this.age -s.age == 0) {        
                return this.name.compareTo(s.name);    
            }
            return -1;
        }

2.定制排序

有时候,用户自定义的类型数据所在的类没有实现Comparable接口或者对于实现了Comparable接口的类而不想按照定义的compareTo()方法进行排序。例如,希望存储在TreeSet集合中的字符串可以按照长度而不是英文字母的顺序来进行排序,这时,可以通过在创建TreeSet集合时就自定义一个比较器来对元素进行定制排序。

    class MyComparator implements Comparator {  
        public int compare(Object obj1, Object obj2) {  // 定制排序方式
            String s1 = (String) obj1;
            String s2 = (String) obj2;
            int temp = s1.length() - s2.length();
            return temp;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值