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());
        System.out.println(p2.hashCode());

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

    /*
        * 完成数组 所有元素的求和 原始写法
        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接口(单列集合)的更多相关文章

  1. java内部类、接口、集合框架、泛型、工具类、实现类

    .t1 { background-color: #ff8080; width: 1100px; height: 40px } 一.内部类 1.成员内部类. (1)成员内部类的实例化: 外部类名.内部类 ...

  2. java 单列集合总结

    Collection 接口 add() remove() contains() clear(); size(); 迭代器遍历(普通迭代器,不能再遍历过程中修改集合的长度) List接口 单列集合 有序 ...

  3. java之List接口(单列集合)

    List接口概述 查询API我们可知:java.util.List 接口继承自 Collection 接口,是单列集合的一个重要分支,习惯性地会将实现了 List 接口的对 象称为List集合.在Li ...

  4. Java之Iterator接口(遍历单列集合的迭代器)

    Iterator接口概述 在程序开发中,经常需要遍历集合中的所有元素.针对这种需求,JDK专门提供了一个接口java.util.Iterator . Iterator 接口也是Java集合中的一员,但 ...

  5. Java之Collection接口(单列集合根接口)

    集合概述 集合到底是什么呢?集合:集合是java中提供的一种容器,可以用来存储多个数据 集合和数组既然都是容器,它们有啥区别呢? 区别1: 数组的长度是固定的. 集合的长度是可变的. 区别2:  数组 ...

  6. Java中的集合(二)单列集合顶层接口------Collection接口

    Java中的集合(二)单列集合顶层接口------Collection接口 Collection是一个高度封装的集合接口,继承自Iterable接口,它提供了所有集合要实现的默认方法.由于Iterab ...

  7. Java学习笔记32(集合框架六:Map接口)

    Map接口与Collection不同: Collection中的集合元素是孤立的,可理解为单身,是一个一个存进去的,称为单列集合 Map中的集合元素是成对存在的,可理解为夫妻,是一对一对存进去的,称为 ...

  8. Java之Map接口(双列集合)

    Map集合概述 现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射.Java提供了专门的集合类用来存放这种对象关系的对 ...

  9. Java学习:单列集合Collection

    集合 学习集合的目标: 会使用集合存储数据 会遍历集合,把数据取出来 掌握每种集合的特性 集合和数组的区别 数组的长度是固定的.集合的长度是可变的. 数组中存储的是同一类型的元素,可以存储基本数据类型 ...

随机推荐

  1. 工具类中注入service和dao

    今天编写了个工具类需要用到service成和dao层的代码 如下: //第一步:需要将工具类注入到容器中 @Component public class RuleUtils { ​ //第二部注入 @ ...

  2. Provider模式应用demo

    参考ObjectPool对象池设计原理还原一个简易的Provider模式. using System; using System.Dynamic; using System.Reflection.Me ...

  3. Vue基础系列(五)——Vue中的指令(中)

    写在前面的话: 文章是个人学习过程中的总结,为方便以后回头在学习. 文章中会参考官方文档和其他的一些文章,示例均为亲自编写和实践,若有写的不对的地方欢迎大家和我一起交流. VUE基础系列目录 < ...

  4. JS 获取元素、修改元素/css样式/标签属性、简单事件、数据类型

    基本使用 写在Script 标签里 引入外部js文件:<script src=" "></script> console.log(" " ...

  5. Android 菜单 使用XML

    @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to th ...

  6. Java面试题_第一阶段(static、final、面向对象、多线程、集合、String、同步、接口、GC、JVM)

    1.1 简述static和final的用法? static:修饰属性,方法,代码块 (1)静态属性:也可叫类变量  类名.属性名  来访问 (共有的类变量与对象无关,只和类有关) 注意:类中的实例变量 ...

  7. eth-trunk学习笔记

    转载自https://blog.csdn.net/qq_38265137/article/details/80404340

  8. php有必要用swoole吗

    在 Swoole 官网的自我介绍是“面向生产环境的 PHP 异步网络通信引擎”,首先 Swoole 它是一个网络应用的开发工具,它支持 Http.TCP.UDP.WebSocket. Swoole 和 ...

  9. 收到一个神盾局的offer,怎么样?

    漫威十一年系列总结性的电影<复联4>正在热映,而衍生出的一部和漫威宇宙关联的美剧<神盾局特工>,今年我也在陆陆续续地看.一开始预期的是一部特工加一些科幻或魔幻元素的剧集,就图看 ...

  10. Android组件体系之Activity启动模式解析

    本文主要分析Activity的启动模式及使用场景. 一.Activity启动模式浅析 1.standard 标准模式,系统默认的启动模式.在启动Activity时,系统总是创建一个新的Activity ...