java之Set接口(单列集合)

Posted wurengen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java之Set接口(单列集合)相关的知识,希望对你有一定的参考价值。

Set接口概述

java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。与 List 接口不同的是, Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。Set 集合有多个子类,这里我们介绍其中的 java.util.HashSet 、 java.util.LinkedHashSet 这两个集合。总结一下Set接口的特点:
  • java.util.Set接口 extends Collection接口
  • 不允许存储重复的元素
  • 没有索引,没有带索引的方法,也不能使用普通的for循环遍历

 HashSet集合介绍

java.util.HashSet 是 Set 接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序 不一致)。 java.util.HashSet 底层的实现其实是一个 java.util.HashMap 支持。HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCode 与 equals 方法。总结一下HashSet集合的特点:
  • java.util.HashSet集合 implements Set接口
  • 不允许存储重复的元素
  • 没有索引,没有带索引的方法,也不能使用普通的for循环遍历
  • 是一个无序的集合,存储元素和取出元素的顺序有可能不一致
  • 底层是一个哈希表结构(查询的速度非常的快)

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

什么是哈希值?

  • 哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址)。
什么是哈希表呢?
  • 在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。 但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,哈 希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找 时间。 简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的。总而言之,JDK1.8引入红黑树大程度优化了HashMap的性能,那么对于我们来讲保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。

如何保证Set集合中的存储的数据唯一性

如果我们往集合中存放自定义的对象,那么保证其唯一, 就必须复写hashCode和equals方法建立属于当前对象的比较方式。

详解Set存储元素原理:

当我们使用Set集合中的add方法往集合中添加元素的时候:

  1. add方法就会调用要添加的元素的hashCode方法,计算要添加元素的哈希值。
  2. 然后add方法在集合中去寻找有没有要添加元素的哈希值,如果没有则添加到集合中去。
  3. 如果发现有(哈希冲突),则要添加的元素会调用equals方法和哈希值相同的元素进行比较,如果返回true。则认定2个元素为同一个,不会在添加到Set集合中去了。如果equals方法返回结果为false,则添加到集合中去。

代码举例

要求: 同名同年龄的人,视为同一个人,只能存储一次

定义Person类

package demo02HashSet;

import java.util.Objects;

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

    public Person() {
    }

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

    //重写equals方法
    @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);
    }

    //重写hashCode
    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name=‘" + name + ‘‘‘ +
                ", 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;
    }
}

定义hashSet集合存储Person类

package demo02HashSet;

import java.util.HashSet;

public class Demo01HashSetPersonTest {
    public static void main(String[] args) {
        //创建HashSet集合存储Person
        HashSet<Person> set = new HashSet<>();
        Person p1 = new Person("小美女", 18);
        Person p2 = new Person("小美女", 18);
        Person p3 = new Person("小美女", 19);
        //查看哈希值
        System.out.println(p1.hashCode());//734175839
        System.out.println(p2.hashCode());//734175839

        System.out.println(p1.equals(p2));//true
        set.add(p1);
        set.add(p2);
        set.add(p3);
        //查看集合里的元素
        System.out.println(set);//[Person{name=‘小美女‘, age=19}, Person{name=‘小美女‘, age=18}]
    }
}

LinkedHashSet类

我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?在HashSet下面有一个子类 java.util.LinkedHashSet ,它是链表和哈希表组合的一个数据存储结构。多了一条链表(记录元素的存储顺序),保证元素有序

可变参数简介

可变参数:JDK1.5之后出现的新特性。如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以使用可变参数。

使用前提:

  • 当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数.

使用格式:

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

               方法体

}

其实这个书写完全等价于

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

         方法体

}

可变参数的原理:

  • 可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数。传递的参数个数,可以是0个(不传递),1,2...多个

可变参数的注意事项

  • 一个方法的参数列表,只能有一个可变参数
  • 如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
代码举例
package demo02HashSet;

public class ChangeArgs {
    public static void main(String[] args) {
        int[] arr = {1, 4, 62, 431, 2};
        int sum = getSum(arr);
        System.out.println(sum);//500

        // 求 这几个元素和 6 7 2 12 2121
        int sum2 = getSum(6, 7, 2, 12, 2121);
        System.out.println(sum2);//2148
    }

    /*
        * 完成数组 所有元素的求和 原始写法
        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;
    }

    //可变参数的特殊(终极)写法
    public static void method(Object... obj) {

    }
}

 

以上是关于java之Set接口(单列集合)的主要内容,如果未能解决你的问题,请参考以下文章

Java之LIst

java之集合概述

Java Collection接口的子接口之Set接口的详解

Java之List

Java面试准备之集合框架

Java 单列集合方法解析与应用