Java基础 第三节 第十三课

举报
我是小白呀iamarookie 发表于 2021/09/09 23:18:07 2021/09/09
【摘要】 Set 接口 概述HashSet 集合介绍HashSet 集合存储数据的结构 (哈希表)存储 HashSet 存储自定义类型元素LinkedHashSet可变参数 概述 java.ut...

概述

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

Set 集合有多个子类, 这里我们介绍其中的java.util.HashSet, java.util.LinkedHashSet这两个集合.

注: Set 集合取出元素的方式可以采用: 迭代器, 增强 for.

HashSet 集合介绍

java.util.HashSet是 Set 接口的一个实现类. 它所存储的元素是不可重复的, 并且元素都是无序的 (即存取顺序不一致). java.util.HashSet底层的实现其实是一个java.util.HashMap支持.

HashSet 是根据对象的哈希值来确定元素在集合中的存储位置, 因此具有良好的存取和查找性能. 保证元素唯一性的方法依赖于: hashCode 与 equals 方法.

我们先来使用一下 Set 集合存储, 看下现象, 在进行原理的讲解.

import java.util.HashSet;

public class Test39 {
    public static void main(String[] args) {
        // 创建 Set 集合
        HashSet<String> set = new HashSet<>();

        // 添加元素
        set.add(new String("cba"));
        set.add("abc");
        set.add("bac");
        set.add("cba");

        // 遍历
        for (String name : set){
            System.out.println(name);
        }
    }
}

输出结果:
cba
abc
bac

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

注: 根据结果我们发现字符串 “cba” 只存储了一个, 也就是说重复的元素 set 集合不存储.

HashSet 集合存储数据的结构 (哈希表)

在 JDK1.8 之前, 哈希表底层采用数组 + 链表实现. 即使用链表处理冲突, 同一 hash 值的链表都存储在一个链表里. 但是当位于一个桶中的元素较多, 即 hash 值相等的元素较多是, 通过 key 值依次查找的效率较低. 而 JDK1.8 中, 哈希表存储采用数组 + 链表 + 红黑树实现. 当链表长度超过阈值 (8) 时, 将链表转换为红黑树, 这样大大减少了查找时间.

简单的来说, 哈希表是由数组 + 链表 + 红黑树 ( JDK1.8 增加了红黑树部分 ) 实现的, 如下图所示:
在这里插入图片描述

存储

流程图:
在这里插入图片描述
总而言之, JDK1.8 引入红黑树大程度优化了 HashMap 的性能. 那么对于我们来讲保证 HashSet 集合元素的唯一, 其实就是根据对象的 hashCode 和 euqals 方法来决定的. 如果我们往集合中存放自定义的对象, 那么保证其唯一, 就必须重复写 hashCode 和 equals 方法建立属于当前对象的比较方式.

HashSet 存储自定义类型元素

给 HashSet 中存放自定义类型元素时, 需要重写对象中的 hashCode 和 equals 方法. 建立自己的比较方式, 才能保证 HashSet 集合中的对象唯一.

创建自定义 Student 类:

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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return getAge() == student.getAge() &&
                Objects.equals(getName(), student.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    } 
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

测试:

import java.util.HashSet;

public class Test40 {
    public static void main(String[] args) {
        // 创建集合对象, 集合中存储Student类型对象
        HashSet<Student> studentHashSet = new HashSet<>();

        // 存储
        Student student = new Student("于谦", 18);
        studentHashSet.add(student);
        studentHashSet.add(new Student("郭德纲",50));

        for (Student temp : studentHashSet){
            System.out.println(temp);
        }
    }
}

输出结果:
Student{name='于谦', age=18}
Student{name='郭德纲', age=50}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

LinkedHashSet

我们知道 HashSet 保证元素唯一, 可是元素存放进去是没有顺序的. 那么我们要保证有序, 怎么办呢?

在 HashSet 下面有一个子类java.util.LinkedHashSet, 它是链表和哈希表组合的一个数据存储结构.

演示代码如下:

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class Test41 {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>();
        set.add("bbb");
        set.add("aaa");
        set.add("abc");
        set.add("bbc");
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

输出结果:
bbb
aaa
abc
bbc

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

可变参数

在 JDK1.5 之后, 如果我们定义一个方法需要接受多个参数, 并且多个参数类型一致. 我们可以对其简化成如下格式:

修饰符 返回值类型 方法名(参数类型... 形参名){  }

  
 
  • 1

其实这个书写完全等价与

修饰符 返回值类型 方法名(参数类型[] 形参名){  }

  
 
  • 1

只是后面这种定义, 在调用时必须传递数组, 而前者可以直接传递数据即可.

同样是代表数组, 但是在调用这个带有可变参数的方法时. 不用创建数组 (这就是简单之处), 直接将数组中的元素作为实际参数进行传递. 编译成的 class 文件, 将这些元素封装到一个数组中, 在进行传递. 这些动作都是在编译 .class 文件时, 自动完成了.

代码演示:

public class Test {
    public static void main(String[] args) {
        int[] arr = { 1, 4, 62, 431, 2 };
        int sum = getSum(arr);
        System.out.println(sum);
       
        int sum2 = getSum(6, 7, 2, 12, 2121);
        System.out.println(sum2);
    }
    /*
     * 完成数组所有元素的求和原始写法

      public static int getSum(int[] arr){
        int sum = 0;
        for(int a : arr){
            sum += a;
        }

        return sum;
      }
    */
    
    //可变参数写法
    public static int getSum(int... arr) {
        int sum = 0;
        for (int a : arr) {
            sum += a;
        }
        return sum;
    }
}

输出结果:
500
2148

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

注: 上述 add 方法在同一个类中, 只能存在一个. 因为会发生调用的不确定性.

注意事项: 如果在方法书写时, 这个方法拥有多参数. 参数中包含可变参数, 则可变参数一定要写在参数列表的末尾位置.

文章来源: iamarookie.blog.csdn.net,作者:我是小白呀,版权归原作者所有,如需转载,请联系作者。

原文链接:iamarookie.blog.csdn.net/article/details/110312099

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。