美文网首页
四 集合——第六节 Set集合

四 集合——第六节 Set集合

作者: 杜艳_66c4 | 来源:发表于2022-05-31 20:19 被阅读0次

1、Set 接口介绍

  • java.util.Set 接口extends Collection 接口
  • 特点:
  • 无序, 不重复的元素。
  • 没有索引,也不能用带索引的方法, 不能使用普通的for循环遍历

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

》》》》》》》》》》》》》》》》》》》》》》》》》

Set 接口子类

2、HashSet集合介绍

  • java.util.HashSet 集合,implements Set接口,java.util.HashSet是Set接口的一个实现类
  • HashSet特点:
  • 无序,存储元素和取出元素的顺序有可能不一致
  • 不重复的元素。
  • 没有索引,也不能用带索引的方法
  • 底层是一个Hash表结构,查询速度非常快

java.util.HashSet底层的实现其实是一个java.util.HashMap支持。

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

package listandset;

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

/**
 * created by apple on 2020/6/20
 * java.util.Set 接口extends Collection 接口
 * 特点:
 * 无序, 不重复的元素。
 * 没有索引,也不能用带索引的方法
 *
 * java.util.HashSet 集合,implements Set接口
 * HashSet特点:
 * 无序,存储元素和取出元素的顺序有可能不一致
 * 不重复的元素。
 *  没有索引,也不能用带索引的方法
 * 底层是一个哈希表结构,查询速度非常快
 *
 */
public class Demo1Set {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        //往集合中添加元素
        set.add(1);
        set.add(2);
        set.add(4);
        set.add(1);
        //没有索引,不能用普通for循环,用迭代器遍历
        Iterator<Integer> it = set.iterator();
        while (it.hasNext()){
            System.out.println(it.next());  // 1 2 4
        }
        //增强for遍历
        System.out.println("=====");
        for (Integer i : set) {
            System.out.println(i);   // 1 2 4
        }
    }

}


2.1、哈希值 十进制的整数

package listandset;

/**
 * created by apple on 2020/6/20
 *
 */
public class Person extends Object{
    //重写hashCode方法 ..那之前的hashCode值是1

    @Override
    public int hashCode() {
        return 1;
    }
}
package listandset;

/**
 * created by apple on 2020/6/20
 * 哈希值: 是一个十进制的整数 ,由系统随机给出(就是对象的地址值,是模拟出来得到的地址,不是数据实际存储的物理地址)
 * 在Object类有一个方法,可以获取对象的hash值
 * int hashCode()
 *    返回对象的hash值
 *    hashCode 方法的源码:
 *     public native int hashCode();
 *     native 代表该方法调用的是本地操作系统的方法
 */
public class DemohashCode {
    public static void main(String[] args) {
        //Person 类继承了Object类, 所以可以使用Object的hashCode方法
        Person person = new Person();
        int h1 = person.hashCode();
        System.out.println(h1);//  1625635731    |1

        Person person2 = new Person();
        int h2 = person2.hashCode();
        System.out.println(h2);//  1580066828    |1

        /*
        toString 方法的源码
         public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
         */
        System.out.println(person);  //listandset.Person@60e53b93   60e53b93 是1625635731的16进制表现形式
        System.out.println(person2);  //listandset.Person@5e2de80c  5e2de80c 是1625635731的16进制表现形式
        System.out.println(person == person2); //false

        //String类的hash值,重写了Object类的hashCode方法,
        String s1 = new String("abc");
        String s2 = new String("abc");
        System.out.println(s1.hashCode());  //96354
        System.out.println(s2.hashCode());  //96354

        System.out.println("重地".hashCode());  // 1179395
        System.out.println("通话".hashCode());  // 1179395
    }
}

1
1
listandset.Person@1
listandset.Person@1
false
96354
96354
1179395
1179395

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

哈希表

红黑树查询速度也快,折半查找
什么是哈希表呢?

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

简单的来说,哈希表是由数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的,如下图所示。


看到这张图就有童鞋要问了,这个是怎么存储的呢?看下图就明白了

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

2.3、HashSet集合存储元素不重复的原理

package listandset;

import java.util.HashSet;

/**
 * created by apple on 2020/6/20
 * Set集合不允许存储重复元素的原理
 */
public class Demo01HashSet {
    public static void main(String[] args) {
        //创建hashSet
        HashSet<String> strings = new HashSet<>();
        strings.add("abc");
        strings.add("abc");
        strings.add("重地");
        strings.add("通话");
        strings.add("abc");
        System.out.println(strings); // [重地, 通话, abc]


    }
}
[重地, 通话, abc]
set集合存储元素不重复的原理

哈希表:数组+ 链表
Set集合在调用add方法时,add方法会调用元素的hashCode方法和equals方法,判断元素是否重复。
set.add("abc");
add方法会调用abc的hashcode方法,计算器哈希值:96354,
在集合中没有找到96354这个值,
集合就会把"abc" 存储到集合中。

set.add("abc")
add方法会调用abc的hashcode方法,计算器哈希值:96354,
在集合中有找到96354这个值,哈希冲突。
调用equals方法,和哈希值相同的元素比较,"abc".equals("abc"),返回true,两个元素的hash值相同,equals方法返回true,认定两个元素相同,就不会把第二个abc放入集合中。
而重地和通话的hashcode值相同,但是没有equals,所以以链表的形式链在下面

2.4、HashSet存储自定义类型元素

没有重写hashcode和equals方法, 不能把同一个人视为同一个人

package listandset;

/**
 * created by apple on 2020/6/20
 */
public class PersonHashSet {
    private String name;
    private int age;

    public PersonHashSet() {
    }

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

    @Override
    public String toString() {
        return "PersonHashSet{" +
                "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;
    }
}
package listandset;

import java.util.HashSet;

/**
 * created by apple on 2020/6/20
 * HashSet存储自定义类型元素
 * set集合保证元素唯一,存储的元素可以是任意类型。
 * 必须重写hashcode equals方法。
 *
 * 要求:同名和同年龄的人视为同一个人,只能存储一次
 */
public class HashSetSavePerson {

    public static void main(String[] args) {
        //创建一个HashSet 集合,存储PersonHashSet
        HashSet<PersonHashSet> set = new HashSet<>();
        PersonHashSet p1 = new PersonHashSet("小美女", 18);
        PersonHashSet p2 = new PersonHashSet("小美女", 18);
        PersonHashSet p3 = new PersonHashSet("小女", 18);
        System.out.println(p1.hashCode());  //1625635731
        System.out.println(p2.hashCode());  //1580066828
        System.out.println(p1.equals(p2));    //哈希值不同 
       System.out.println(p1 == p2);  //false,equlas 方法默认比较两个对象的地址值 ,比较地址值
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(set);  //没有重写HashCode方法和equals方法。  [PersonHashSet{name='小美女', age=18}, PersonHashSet{name='小美女', age=18}, PersonHashSet{name='小女', age=18}]

    }
}
输出:
1625635731
1580066828
false
false
[PersonHashSet{name='小美女', age=18}, PersonHashSet{name='小美女', age=18}, PersonHashSet{name='小女', age=18}]

重写hashCode和equals 方法,可以比较出来是同一个人,重写的方法是自动生成的哦

package listandset;

import java.util.Objects;

/**
 * created by apple on 2020/6/20
 */
public class PersonHashSet {
    private String name;
    private int age;

    public PersonHashSet() {
    }

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

    @Override
    public String toString() {
        return "PersonHashSet{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, 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;
    }
}

最后输出:

734175839
734175839
true
false
[PersonHashSet{name='小女', age=18}, PersonHashSet{name='小美女', age=18}]

》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》

3、LinkedHashSet

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

package listandset;

import java.util.HashSet;
import java.util.LinkedHashSet;

/**
 * created by apple on 2020/6/20
 * java.util.LinkedHashSet 集合extends HashSet 集合
 * LinkedHashSet特点:
 * 底层是一个哈希表(数组+ 链表/红黑树)+ 链表,多了一条链表,用来记录元素的存储顺序,保证元素有序 HashSet
 */
public class DemoLinkedHashSet {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("www");
        set.add("abc");
        set.add("abc");
        set.add("ifta");
        System.out.println(set);  //[abc, www, ifta]  无序,不允许重复

        LinkedHashSet<String> set1 = new LinkedHashSet<>();
        set1.add("www");
        set1.add("abc");
        set1.add("abc");
        set1.add("ifta");
        System.out.println(set1);  //[www, abc, ifta]  有序的,不允许重复
    }

}
[abc, www, ifta]
[www, abc, ifta]

4、可变参数

package listandset;

/**
 * created by apple on 2020/6/20
 * 可变参数:在jdk1.5之后的出现的新特性
 * 使用前提:
 * 当方法的参数列表【数据类型】已经确定,但【参数个数】不确定,可以使用可变参数
 * 使用格式:  定义方法时使用
 * 修饰符 返回值类型 方法名(数据类型... 变量名){}
 * 可变参数的原理:
 * 底层是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数,传递的参数格式可以是0个(不传递),1,2,。。。多个
 * 可变参数的注意事项:
 * 1、一个方法的参数列表,只能有一个可变参数
 * 2、如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
 */
public class DemoVar {
    public static void main(String[] args) {
        int i = addint();
        System.out.println("没有参数:" + i);
        int i2 = addint(10,20,30,40,50);
        System.out.println("一个参数:" + i2);

    }
    //定义一个方法,计算两个int类型整数的和

    /*
     定义计算(0-n)整数和的方法。
     数据类型确定了,int
     参数的个数不确定,不知道要计算几个整数的和,就可以使用可变参数
     add() 会创建一个长度为0 的数组。  new int[0]
     add(10) 会创建一个长度为1 的数组,存储传递过来的参数  new int[]{10}
传递过来几个参数,数组长度就是几
     */
   /* public static int addint(int a ,int b) {

        return a + b;
    }*/
    public static int addint(int...arr) {
        //System.out.println(arr);  //数组的地址[I@60e53b93,底层是一个数组
       // System.out.println("数组长度:" + arr.length); //0 ,长度为0 的数组 。1
        //定义一个初始化的变量,记录累加求和,
        int sum = 0;
        //遍历数组,获取数组中的元素
        for (int i : arr) {
            //累加求和
            sum+= i;
        }
        return sum;
    }
}
没有参数:0
一个参数:150

相关文章

网友评论

      本文标题:四 集合——第六节 Set集合

      本文链接:https://www.haomeiwen.com/subject/eklgxktx.html