Java基础——常用对象API(4):集合框架3:Set集合

本文详细介绍了Java中Set系列集合的特点,包括HashSet、LinkedHashSet和TreeSet的区别。着重讲解了Set集合如何处理元素去重,特别是对于自定义对象的处理。此外,解释了Set集合无序的原因,并提供了具体的代码示例,展示了如何通过重写hashCode()和equals()方法,以及实现Comparable或Comparator接口来控制元素排序和去重。

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

在这里插入图片描述

1.Set系列集合特点

1.1 与List比较

List:有序,可重复,有索引
Set:无序,不重复,无索引

1.2 HashSet

添加的元素无序,不重复,无索引。内部数据结构是哈希表,是不同步的。

1.3 LinkedHashSet

添加的元素,有序,不重复,无索引。

1.4 TreeSet

按照大小默认升序排序,不重复,无索引。

关于排序:

  1. 字符串按照首字符排序。首字符相同时,看第二位,依此类推。

  2. 有值特性的元素(int,float等),按照数值大小排序。

  3. 自定义的引用类型,可以:
    --------- 在对象类中重写Comparable接口的compareTo()方法。
    --------- 为集合设置比较器对象,即重写Comparator接口的Compare()方法。

如果两样都被实现,优先使用比较器的比较规则。

ps: 具体代码实现,详见3.2.2
在这里插入图片描述


2.Set集合的两个问题

2.1 Set集合添加的元素如何去重合?

  • 对于有值特性(String,Integer,int)的,直接判断进行去重复。
  • 对于引用数据类型的类对象:
  1. hashCode
  2. equals

首先Set集合会让两两对象,先调用自己的hashCode()方法,判断两个对象的哈希值是否相同;如果相同,再用equals()方法判断内容是否相同。

如果哈希值不同,则无需判断内容是否相同。

2.2 Set集合无序的原因

Set集合添加元素无序是因为其底层用哈希表存储元素:

JDK1.8之前,哈希表 = 数组 + 链表 + (哈希算法)

JDK1.8之后,哈希表 = 数组 + 链表 + 红黑树 +(哈希算法)

哈希算法:

  1. 先获取元素对象的哈希值
  2. 用哈希值对底层数组的长度取余
  3. 取余的结果作为该元素在底层数组的索引位置
  4. 存入该元素对象
    ps:如果索引位置相同,在该数组的位置形成链,依次连接。
    在这里插入图片描述

用哈希表存储元素,增删改查性能好,但它无序,不重复。
同时考虑到链表可能过长影响性能,在JDK1.8之后,当链表长度超过8时,链表会 转成红黑树

ps:LinkedHashSet在底层依然是用哈希表存储元素,

但是每个元素额外带一个链维护添加顺序,顺着这条链,有序查找输出元素。


3. 代码示例

3.1 用HashSet存储自定义对象

规定:如果学生类的姓名和年龄一样,则视为同一个对象。

分析:即使Student对象内的姓名和年龄完全相同,因为哈希值是根据对象地址得出的,所以把数值完全相同的对象往HashSet集合存储时,都会被存进去。但是现在规定姓名和年龄一样,则视为同一个对象,也就是存进去时前一个要被去掉。‘

所以
通过重写hashCode()方法和equals()方法可实现规定内容。

做法:重写方法时,让哈希值根据name和age得出;如果哈希值相同,重写后的equals方法判断name和age的值是否相同,若相同,则该对象不会进入HashSet。

import java.util.HashSet;
import java.util.Iterator;

class Student {
    private String name;
    private int age;

    Student() {

    }

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

    @Override
    public int hashCode() {
        return name.hashCode()+age;
    }

    @Override
    public boolean equals(Object obj) {
        Student s = (Student)obj;
        return this.name.equals(s.name) && this.age==s.age;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}

public class HashCodeDemo {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();

        hashSet.add(new Student("abc1",21));
        hashSet.add(new Student("abc2",22));
        hashSet.add(new Student("abc3",23));
        hashSet.add(new Student("abc4",24));
        hashSet.add(new Student("abc1",21));

        Iterator iterator = hashSet.iterator();
        while(iterator.hasNext()) {
            Student s = (Student)iterator.next();
            System.out.println( s.getName()+"————"+s.getAge() );
        }
    }
}

3.2 TreeSet代码示例

3.2.1 处理带值特性的对象(字符串等)
public class Demo {
    public static void main(String[] args) {

        TreeSet a = new TreeSet();
        a.add("abc1");
        a.add("abc2");
        a.add("abc3");
        a.add("abc3");
        
        Iterator iterator = a.iterator();
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
            //abc1
			//abc2
			//abc3
        }
    }
}
3.2.2 处理无值特性的对象

必须要:实现Comparable接口或者实现Comparator接口

否则报错,因为不知道排序规则

3.2.2.1 实现Comparable接口

Student类中实现Comparable接口,重写compareTo方法

import java.util.Iterator;
import java.util.TreeSet;

class Student implements Comparable {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

	@Override
    public int compareTo(Object obj) {//根据年龄排序,如果年龄相同,按照名字排序

        Student s = (Student)obj;
        
        int temp = this.age-s.age;

        return temp == 0 ? this.name.compareTo(s.name) : temp;
    }
}

public class Demo {
    public static void main(String[] args) {

        TreeSet a = new TreeSet();

        a.add(new Student("zxc1",21));
        a.add(new Student("zxc2",22));
        a.add(new Student("zxc5",22));
        a.add(new Student("zxc3",23));
        a.add(new Student("zxc3",23));

        Iterator iterator = a.iterator();
        while(iterator.hasNext()) {
            Student s = (Student)iterator.next();
            System.out.println(s.getName()+"——"+s.getAge());
        }
    }
}
3.2.2.2 实现Comparator接口
1.创造一个实现Comparator接口的类

集合自身具备比较功能(Comparator比较器):重新定义一个类实现Comparator接口,重写compare方法;创建该类的对象作为参数放进TreeSet中。

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class Demo {
    public static void main(String[] args) {

        TreeSet a = new TreeSet(new ComparatorByAge());//在构造方法内,创建带比较器的对象作为参数

        a.add(new Student("zxc1",21));
        a.add(new Student("zxc2",22));
        a.add(new Student("zxc5",22));
        a.add(new Student("zxc3",23));
        a.add(new Student("zxc3",23));

        Iterator iterator = a.iterator();
        while(iterator.hasNext()) {
            Student s = (Student)iterator.next();
            System.out.println(s.getName()+"——"+s.getAge());
        }
    }
}

class ComparatorByAge implements Comparator {
    public int compare(Object o1,Object o2) {
        Student s1 = (Student)o1;
        Student s2 = (Student)o2;
        int temp = s1.getAge()- s2.getAge();

        return temp==0 ? s1.getName().compareTo(s2.getName()) : temp;
    }
}
2.使用匿名内部类

用匿名内部类可以省去再写一个实现Comparator接口的类,更加方便

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

public class Demo {
    public static void main(String[] args) {

        TreeSet<Student> a = new TreeSet<Student>(new Comparator<Student>() {//匿名内部类
            @Override
            public int compare(Student s1,Student s2) {
                int temp = s1.getAge()-s2.getAge();
                return temp==0 ? s1.getName().compareTo(s2.getName()) : temp;
            }
        });

        a.add(new Student("zxc1",21));
        a.add(new Student("zxc2",22));
        a.add(new Student("zxc5",22));
        a.add(new Student("zxc3",23));
        a.add(new Student("zxc3",23));

        Iterator iterator = a.iterator();
        while(iterator.hasNext()) {
            Student s = (Student)iterator.next();
            System.out.println(s.getName()+"——"+s.getAge());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值