TreeSet(Comparable和Comparator)和HashSet

本文深入探讨了TreeSet的底层实现原理,基于红黑树的有序存储机制,以及如何通过实现Comparable接口或使用Comparator接口来定制自定义类的排序规则。详细解释了比较器的使用方法,包括升序和降序排列,以及自定义类在TreeSet中的存储条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TreeSet----底层基于红黑树

  • 有序存储
  • 不允许元素重复,并且按照元素升序排序,不允许存放null
  • 自定义类要想保存到TreeSet中,
    要么实现Comparable接口,要么向TreeSet传入比较器(Compartor接口)

实现了Comparable接口的类可以直接存放在TreeSet或TreeMap中。

public int compareTo(T o);
返回值三种情况:
返回正数:表示当前对象大于目标对象
返回0:表示当前对象等于目标对象
返回负数:表示当前对象小于目标对象

实现Comparable接口
class Person implements Comparable<Person>
{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Person o) {
        if(this.age>o.age)
            return 1;  //那么是升序
        else if(this.age<o.age)
            return -1;
         return this.name.compareTo(o.name);  //String类有自己的compareTo方法
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Comparator1 {
    public static void main(String[] args) {
        Set<Person> set=new TreeSet<>();
        Person p1=new Person("pick",20);
        Person p2=new Person("bai",28);
        set.add(p1);
        set.add(p2);
        System.out.println(set);
    }
}

Comparator(外部排序接口)
若要控制某个自定义类的顺序,而该类本身不支持排序(该类本身没有实现Comparable接口)。我们可以建立一个该类的比较器来进行排序。比较器实现Comparator接口即可。
“比较器”:实现了Comparator接口的类作为比较器,通过该比较器来进行类的排序。
int compare(T o1,T o2); ----因为在类外进行比较,有2个参数
返回值三种情况:
返回正数:表示o1大于o2
返回0:表示01等于o2
返回负数:表示o1小于o2

升序,小的排在前面,Comparator两个抽象方法,但只需要实现compare方法,equals方法object类实现
升序:return o1.getAge()- o2.getAge() , 返回值大于0,说明o1大,把o1放在o2后 o1> o2 return 1
降序:return o2.getAge()- o1.getAge() , o1>o2 return -1

jdk默认是升序,是基于:
小于 return -1
等于 return 0
大于 return 1


降序:
小于 return 1
等于 return 0
大于 return -1

//利用比较器
class Person2
{
    private String name;
    private int age;

    public Person2(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

//升序排
//jdk默认是升序,是基于:
//<    return -1
// =   return 0
// >  return 1
class AscSort implements Comparator<Person2>
{
    @Override
    public int compare(Person2 o1, Person2 o2) {
        if(o1.getAge()>o2.getAge())
            return 1;
        else if(o1.getAge()==o2.getAge())
            return 0;
        return -1;
    }
}

//降序排
//降序:<  return 1
//    = return 0
//   > return -1
class DescSort implements Comparator<Person2>
{
    @Override
    public int compare(Person2 o1, Person2 o2) {
        if(o1.getAge()>o2.getAge())
            return -1;
        else if(o1.getAge()==o2.getAge())
            return 0;
        return 1;
    }
}
public class Comparator1 {
    public static void main(String[] args) {
        Set<Person2> set=new TreeSet<>(new DescSort());  //降序排
        Person2 p1=new Person2("pick",20);
        Person2 p2=new Person2("bai",28);
        set.add(p1);
        set.add(p2);
        System.out.println(set); //[Person{name='bai', age=28}, Person{name='pick', age=20}]
    }
}

Comparable接口与Compartor接口区别:
1.在Java中,若想实现自定义类的比较,提供了以下两个接口
java.lang.Comparable接口(内部比较器)—排序接口:
若一个类实现了Comparable接口,即自己排序,就意味着该类支持排序
存放该类的Conllection或数组,可以直接通过Collections.sort()或Arrays.sort进行排序
2.Comparator接口是比较器接口,类本身不支持排序,专门有若干个第三方的比较器(实现了Comparator接口的类)来进行类的排序,是一个外部比较器(策略模式)
使用Comparator更加灵活,只需要根据不同的需求 用不同类实现Comparator,不影响比较的类

重复元素的判断:
TreeSet和TreeMap依靠Comparator或Comparable接口来区分重复元素(返回值=0即是重复)

自定义类要保存在TreeSet或TreeMap中:
1.要么该类直接实现Comparable接口,覆写compareTo方法
2.或者实现一个比较器传入TreeSet或TreeMap来进行外部比较

HashSet与HashMap并不依赖比较接口。此时要想区分自定义元素是否重复,需要同时覆写equals与hashCode方法。

若两个对象的equals方法返回true,他们的hashCode必然要保证相等。但是两个对象的hashCode相等,equals不一定相等。
当且仅当equals与hashCode方法均返回true,才认为两个对象真正相等

HashSet和HashMap判断元素是否重复:
先调用hashCode计算出对象hash码决定存放的数据桶
而后使用equals来比较元素是否相等,若相等,则不再放置元素,若equals返回false,则在相同桶之后,使用链表将若干元素链起来。
object类提供的hashCode方法默认使用对象的地址进行hash

//覆写hashCode
public int hashCode()
{
    return Objects.hash(age,name);  Objects:所有类父类提供的若干方法 
     //当两个元素的属性相等,则一定放在同一个桶中
}

Set接口是Value值相同的Map集合,先有Map才有Set
TreeSet是TreeMap
TreeMap区分key值重复,需要实现Comparable或实现一个比较器
HashSet是HashMap
HashMap区分key值重复,需要实现equals和HashCode方法

### 特点对比 #### HashSet特点 HashSet通过哈希表实现,因此其内部元素无序排列[^1]。对于`add`, `remove`, `contains`方法而言,由于利用了哈希机制,这些操作的时间复杂度接近常数级别O(1),这使得在大数据量情况下性能表现优异。 #### TreeSet特点 TreeSet基于红黑树结构构建而成,这意味着它能够保持自然顺序或是根据指定比较器定义的顺序存储数据。与HashSet不同的是,在执行插入、删除以及查找等基本集合操作时,TreeSet提供了对数时间复杂度O(log n)[^2]。此外,为了正确地使用TreeSet中的对象作为键值,通常需要确保该类实现了Comparable接口并重写了compareTo()方法来满足特定条件[^3]。 ### 使用示例 以下是两个简单的Java代码片段展示了如何创建操作这两种类型的集合: #### 创建HashSet实例及其基础操作 ```java import java.util.HashSet; public class Main { public static void main(String[] args) { HashSet<String> set = new HashSet<>(); // 添加元素到HashSet中 set.add("Apple"); set.add("Banana"); System.out.println(set.contains("Apple")); // 输出 true // 移除某个元素 set.remove("Banana"); for (String item : set){ System.out.println(item); } } } ``` #### 创建TreeSet实例及其特性展示 ```java import java.util.TreeSet; import java.util.Comparator; class CustomComparator implements Comparator<Integer>{ @Override public int compare(Integer o1, Integer o2) { return -(o1.compareTo(o2)); // 反向排序 } } public class Main { public static void main(String[] args) { TreeSet<Integer> treeSetAsc = new TreeSet<>(); TreeSet<Integer> treeSetDesc = new TreeSet<>(new CustomComparator()); // 向升序TreeSet添加元素 treeSetAsc.addAll(java.util.Arrays.asList(50, 40, 70)); // 打印结果会按照从小到大排序 System.out.println(treeSetAsc); // 向自定义降序TreeSet添加相同元素 treeSetDesc.addAll(java.util.Arrays.asList(50, 40, 70)); // 结果则按从大到小显示 System.out.println(treeSetDesc); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值