Java基础——单列集合(二)

回顾

上篇文章Java基础——单列集合(一)主要讲了单列集合中的List系列集合,这篇文章紧接着来介绍Set系列集合,让我们先来回顾一下集合体系:

再来看看不同集合的底层结构:

Set集合概述 

HashSet

概念

  • HashSet集合底层采用哈希表存储数据
  • 哈希表是一种对于增删改查数据性能都较好的结构

哈希表组成:

JDK8之前:数组+链表

JDK8开始:数组+链表+红黑树

链表和红黑树都是为了解决哈希冲突而存在的

哈希值

如果没有hashCode重写,则利用地址值计算哈希值

底层原理

JDK8以前

JDK8以后

  • 当数组存储数据的长度达到(默认长度*加载因),数组就会扩容到原来的二倍
  • 当链表长度大于8而且数组长度大于等于64时,链表自动转换为红黑树

HashSet组成:数组+链表+红黑树

三个特点

1、HashSet的无序:存和取的顺序不一样

哈希值是随机的,则存入顺序是随机的

2、HashSet没有索引

因为有链表和红黑树的存在(无索引)

3、HashSet不重复

利用hashCode方法和eqauls方法保证数据去重

代码实现

先创建Student1类,并在其中重写equals和hashCode方法

public class Student1 {
    private String name;
    private int age;

    public Student1() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student1 student1 = (Student1) o;
        return age == student1.age && Objects.equals(name, student1.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

在测试类中将Student1类的对象添加进HashSet集合中

public class HashSet {
    public static void main(String[] args) {
        Student1 s1 = new Student1("zhangsan",23);
        Student1 s2 = new Student1("wangwu",24);
        Student1 s3 = new Student1("lisi",25);
        Student1 s4 = new Student1("zhangsan",23);

        java.util.HashSet<Student1> hs = new java.util.HashSet<>();

        System.out.println(hs.add(s1));
        System.out.println(hs.add(s2));
        System.out.println(hs.add(s3));
        System.out.println(hs.add(s4));//去重,无法添加

        System.out.println(hs);//存储顺序和添加顺序不一样
    }
}

1、由于自动去重,所以s4无法添加

2、 由于存取顺序不一样,打印出来的顺序不是按照添加顺序:

LinkedHashSet

底层原理

如果只要求数据去重,默认使用HashSet

如果要求去重且存取有序,使用LinkedHashSet

TreeSet

特点

  • 不重复,无索引,可排序
  • 可排序:按照元素的默认规则(从小到大)排序(自动排序)
  • TreeSet集合底层基于红黑树的数据结构实现排序的,增删改查性能都较好

默认规则

TreeSet的两种比较方式

一、默认排序:Javabean类实现Comparable接口指定比较规则

TreeSet底层是红黑树而不是哈希表,所以不用重写hashCode和eqauls方法

TreeSet要在(集合中存储的数据类型如果是类)中实现Comparable接口,并重写compareTo方法

二、比较器排序:创建TreeSet对象时候,传递比较器Comparator指定规则

TreeSet构造方法中,允许Comparator接口作为参数,使其中的comparator方法作为排序规则

使用匿名内部类的方式,创建Comparator接口实现类对象并重写compare方法

string类中已重写过compareTo方法(按照字典序排序)

如何选择

默认情况下用第一种排序方式,如果第一种不能满足则使用第二种

如果两种方式同时存在,以第二种为准

补充

compare和compareTo方法的区别:

  1. 所属的接口/类

    • compareTo 方法是 Comparable 接口的一部分。当你想要一个类的对象能够相互比较时,你通常会让这个类实现这个接口。这个方法是由类的实例(对象)调用的。
    • compare 方法是 Comparator 接口的一部分。当你想要一个其它类(不是待比较对象的类)来定义如何比较对象时,你会实现这个接口。这个方法是由 Comparator 的实例调用的。
  2. 使用场景

    • compareTo:当你想要类本身定义其自然排序顺序时,你会在类中实现 Comparable 接口,并提供 compareTo 方法的实现。例如,String 类实现了 Comparable<String> 接口,允许你直接使用字符串进行比较和排序。
    • compare:当你不想在类本身中定义排序顺序时,你会创建一个实现 Comparator 接口的类,并提供 compare 方法的实现。
  3. 方法签名

    • compareTo 的典型签名是 int compareTo(T o),其中 T 是类自身的类型,并且方法返回一个整数,表示当前对象与参数对象之间的比较结果。
    • compare 的典型签名是 int compare(T o1, T o2),其中 T 是要比较的对象的类型,并且方法返回一个整数,表示第一个参数对象与第二个参数对象之间的比较结果。
  4. 调用方式

    • 对于 compareTo,你通常会让A类实现comparable接口并重写compareTo方法,假设a、b都是A类的对象,这样调用它:a.compareTo(b)。
    • 对于 compare,你通常会这样调用它(假设你有一个 Comparator 的实例叫做c):c.compare(object1, object2)。

五种集合总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值