HashSet、LinkedHashSet

本文详细介绍了JavaSet系列集合(HashSet、LinkedHashSet和TreeSet)的特点,包括它们的无序性、不重复性、无索引以及底层实现原理。还探讨了哈希表、哈希值计算、对象的equals和hashCode方法的应用,以及如何利用这些特性处理自定义对象的去重和顺序问题。
摘要由CSDN通过智能技术生成

一、Set系列:

1.Set系列集合:

无序:存取顺序不一样

不重复:可以去除重复

无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引获取元素

2.Set集合的实现类:

HashSet:无序,不重复,无索引

LinkedHashSet:有序,不重复,无索引

TreeSet:可排序,不重复,无索引

3.Set接口中的方法基本上与Collection的API一致

练习:存储字符串并遍历

利用Set系列的集合,添加字符串,并使用多种方式遍历

①迭代器②增强for③Lambda表达式

package a06mysetdemo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Consumer;

public class SetDemo1 {
    public static void main(String[] args) {
        //1.创建一个Set集合的对象
        Set<String> s=new HashSet<>();
        //2.添加元素
        //如果当前元素是第一次添加,那么可以添加成功,返回true
        //如果当前元素是第二次添加,那么添加失败,返回false
        boolean r1 = s.add("zhangsan");
        boolean r2 = s.add("zhangsan");
        s.add("lisi");
        s.add("wangwu");
//       System.out.println(r1);//true
//        System.out.println(r2);//false
//        System.out.println(s);//[zhangsan]

        //3.打印集合
        //无序的,存和取的顺序有可能不一样
        //System.out.println(s);//[lisi, zhangsan, wangwu]

        //迭代器遍历
//        Iterator<String> it = s.iterator();
//        while(it.hasNext()){
//            String str = it.next();
//            System.out.println(str);
//        }

        //增强for
//        for (String str : s) {
//            System.out.println(str);
//        }

        //lambda表达式
        s.forEach( str-> System.out.println(str));
    }
}

运行结果:

二、HashSet

1.底层原理:

HashSet集合底层采取哈希表存储数据

哈希表是一种对于增删改查数据性能都较好的结构

2.哈希表组成:

JDK8以前:数组+链表

JDK8以后:数组+链表+红黑树

3.哈希值:对象的整数表现形式

①根据hashCode方法算出来的int类型的整数

②该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算

③一般情况下会重写hashCode方法,利用对象内部的属性值计算哈希值

4.对象的哈希值特点:

如果没有重写hashCode方法,不同对象的哈希值是不同的

如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的

在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)

代码演示:Student类:

package a06mysetdemo;

import java.util.Objects;

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

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.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;
    }

    //重写hashCode


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

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

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

测试类:

package a06mysetdemo;

public class HashSetDemo1 {
    public static void main(String[] args) {
        //1.创建对象
        Student s1=new Student("zhangsan",23);
        Student s2=new Student("zhangsan",23);

        //2.如果没有重写hashCode方法,不同对象的哈希值是不同的
        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

    }
}

 

没重写hashCode方法之前运行结果:

 

重写hashCode方法之后运行结果:

 5.HashSet JDK8 以前,JDK8以后底层原理

①创建一个默认长度16,默认加载因子为0.75的数组,数组名table

加载因子:扩容时机:当数组里面存了16*0.75=12个元素的时候,此时数组就会扩容成原来的两倍16*2=32

②根据元素的哈希值跟数组的长度计算出应存入的位置

哈希值计算公式:int index=(数组长度-1) & 哈希值;

③判断当前位置是否为null,如果是null直接存入

④如果位置不为null,表示有元素,则调用equals方法比较属性值

⑤一样:不存  不一样:存入数组,形成链表

              JDK8以前:新元素存入数组,老元素挂在新元素下面

              JDK8以后:新元素直接挂在老元素下面

JDK8以后:当链表长度大于8而且数组长度大于等于64时,当前链表会自动转化成红黑树

6.HashSet的三个问题:
问题1:HashSet为什么存和取顺序不一样

HashSet是从0索引一条链表,一条链表遍历的

问题2:HashSet为什么没有索引

因为HashSet 底层有数组链表红黑树,一条链表上有多个元素规定谁是0索引不好规定,所以没有

问题3:HashSet是利用什么机制保证数据去重的

利用hashCode方法和equals方法,利用hashCode方法得到哈希值,得到哈希值就能确定当前元素是在数组的哪个位置,再调用equals方法去比较对象内部属性值是否相同

所以如果HashSet里面存储的是自定义对象,那么一定要重写HashCode和equals方法

7.练习:利用HashSet集合去除重复元素

需求:创建一个存储学生对象的集合,存储多个学生对象,实用程序实现在控制台遍历该集合

要求:学生对象的成员变量值相同,我们就认为是同一个对象

Student类:

package a06mysetdemo;

import java.util.Objects;

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

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.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;
    }

    //重写hashCode


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

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

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

测试类:

package a06mysetdemo;

import java.util.HashSet;

public class HashSetDemo2 {
    public static void main(String[] args) {
        //1.创建三个学生对象
        Student s1=new Student("zhangsan",23);
        Student s2=new Student("lisi",24);
        Student s3=new Student("wangwu",25);
        Student s4=new Student("zhangsan",23);

        //2.创建集合用来添加学生
        HashSet<Student> hs=new HashSet<>();

        //3.添加元素
        System.out.println(hs.add(s1));
        System.out.println(hs.add(s2));
        System.out.println(hs.add(s3));
        System.out.println(hs.add(s4));
        //4.打印集合
        System.out.println(hs);
    }
}

运行结果:

三、LinkedHashSet

1.底层原理:有序,不重复,无索引

这里的有序指的是保证存储和取出的元素顺序一致 

原理:底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序

(图片来自哔站黑马程序员)

 代码演示:

Student类:

package a06mysetdemo;

import java.util.Objects;

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

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.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;
    }

    //重写hashCode


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

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

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

 测试类:

package a06mysetdemo;

import java.util.LinkedHashSet;

public class LinkedHashSetDemo1 {
    public static void main(String[] args) {
        //1.创建4个学生对象
        Student s1=new Student("zhangsan",23);
        Student s2=new Student("lisi",24);
        Student s3=new Student("wangwu",25);
        Student s4=new Student("zhangsan",23);

        //2.创建集合的对象
        LinkedHashSet<Student> lhs=new LinkedHashSet<>();
        //3.添加元素
        System.out.println(lhs.add(s1));
        System.out.println(lhs.add(s2));
        System.out.println(lhs.add(s3));
        System.out.println(lhs.add(s4));

        //4.打印集合
        System.out.println(lhs);
    }
}

运行结果:

注意:如果数据去重,默认使用HashSet;如果要求去重且存取有序, 才使用LinkedHashset

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值