【Java基础进阶笔记】- Day03 - 第四章 Set接口

Java基础进阶笔记 - Day03 - 第四章 Set接口

系统:Win10
JDK:1.8.0_121
IDE:IntelliJ IDEA 2017.3.7

java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是Collection接口更加严格。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复
Set集合有多个子类,这里主要学习其中的java.util.HashSetjava.util.LinkedHashSet这两个集合

小知识:Set集合取出元素的方式可以采用:迭代器、增强for

4.1 HashSet集合介绍

java.util.HashSetSet接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不能保证一致)。java.util.HashSet底层的实现其实是一个java.util.HashMap支持。
HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCodeequals方法
这里我们先使用一下Set集合存储,再进行原理的分析:

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

        // 添加元素
        set.add("abc");
        set.add("bac");
        set.add("cab");
        set.add("abc");
        set.add("你好");
        System.out.println(set);
        // 用增强for遍历集合
        for (String s : set) {
    
    
            System.out.println(s);
        }
    }
}

运行结果:
在这里插入图片描述

小知识:根据结果我们发现字符串abc只存储了一次,也就是说重复的元素set集合不存储,而且输出的顺序和我们存储的顺序也不一致,说明是无序的

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


什么是哈希表?
在JDK1.8之前,哈希表底层采用数组+链表实现的,即使用链表处理冲突,同一hash值的不同元素都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而在JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阙值(8)时,将链表转换为红黑树,这样可以大大缩减查询时间
简单来说:哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示
**加粗样式**

如果对上面这图的数据存储过程有点疑问的话,我们可以结合一个存储流程图来理解:
在这里插入图片描述

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

4.3 HashSet存储自定义类型元素

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

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

    public Person() {
    
    
    }

    public Person(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 == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

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

    // 用于等会直接打印Person对象
    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

创建测试方法

public class HashSetDemo02 {
    
    
    public static void main(String[] args) {
    
    
        // 创建集合对象,用于存储Person对象
        HashSet<Person> set = new HashSet<>();

        // 往集合中添加对象
        set.add(new Person("张三", 18));
        set.add(new Person("李四", 18));
        set.add(new Person("李四", 19));
        set.add(new Person("张三", 18));

        // 使用增强for打印set集合
        for (Person person : set) {
    
    
            System.out.println(person);
        }
    }
}

运行结果:
在这里插入图片描述

4.3 LinkedHashSet

我们知道HashSet保证元素唯一,可是元素存放进去和取出来的顺序是不能保证一致的,如果我们需要存取有序,该怎么办?
在HashSet下面有一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。与HashSet不同之处在于,它维护着一个运行于所有条目的双重链表。此链表定义了迭代顺序,即按照将元素插入set中的顺序进行迭代
代码演示如下:

public class LinkedHashSetDemo01 {
    
    
    public static void main(String[] args) {
    
    
        Set<String> linkedSet = new LinkedHashSet<>();
        linkedSet.add("张三");
        linkedSet.add("李四");
        linkedSet.add("王二");
        linkedSet.add("麻子");
        linkedSet.add("张三");

        // 使用迭代器进行迭代
        Iterator<String> it = linkedSet.iterator();
        while (it.hasNext()) {
    
    
            String str = it.next();
            System.out.println(str);
        }
    }
}

运行结果:
在这里插入图片描述

4.4 可变参数

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

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

其实这个书写完全等价于

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

只是后面这种定义,在调用时必须传递数组,而前者可以直接传递数据即可
JDK1.5以后,出现了简化操作。…用在参数上,称之为可变参数
同样是代表数组,但是在调用这个带有可变参数的方法时,不用创建数组(这就是简单之处),直接将数组中的元素作为实际参数进行传递,其实编译成的class文件,将这些元素先封装到一个数组中,再进行传递。这些动作都在编译.class文件时,自动完成了
代码演示:

/*
 写一个方法,随便输入几个整数,返回这几个数的和
*/
public class ChangeParamDemo01 {
    
    
    public static void main(String[] args) {
    
    
        int res = add(1, 3, 5, 7, 9);
        System.out.println(res);
    }

    // 当方法参数的:类型确定,数量不确定时,使用可变参数
    private static int add(int...param) {
    
    
        int sum = 0;
        for (int i : param) {
    
    
            sum = sum + i;
        }
        return sum;
    }
}

运行结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_35132089/article/details/112000215