Set接口 HashSet hashCode() LinkedHashSet TreeSet

1.3Set接口
1.set接口概述

Set接口是Collection接口另外一个常用子接口,Set接口描述的是一种比较简单的集。集合中的对象并不按特定的方式排序,并且不能保存重复的对象,也就是说Set接口可以存储一组唯一、无序的对象。

1.1set集合特点:

1.无索引:不能通过索引index这个参数把元素找出来,无 Object get(int index)
2.不可重复
它的实现类:
HashSet :无序,
LinkedHashSet:有序

​ Set接口常用的实现类有HashSet。

1.底层的数据接口存储(哈希表):
数组+单向链表/红黑树 (当链表节点数>8,同时数组元素数量>=64,会把链表转换为红黑树)

2.查询效率快,增删速度也不慢

3.无索引: 指的是不能通过索引来获取元素: 返回值Object get(int index)

4.元素不可重复:存储元素不可重复依赖我们元素所属类的hashCode()和equals()

5.无序:不保证存入和取出的顺序一致

6.线程不安全,但效率贼高

HashSet的底层是靠HashMap支持
HashSet实现set接口,由HashMap支持

public HashSet() {
          map = new HashMap<>();
      }
   public HashMap() {
          this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
      }

此构造方法 :没有添加元素时,是一个空集合,当添加第一个元素时,构造一个默认初始容量(16)和默认加载因子(0.75)的空HashMap集合

java.util.HashMap<K,V>集合:是一个双列集合,一次存储一对元素

问题来了?
HashSet存储数据一次存一个,但是HashMap一次存储一对,内部HashSet集合如何把一个元素变成一对?

其实,当使用HashSet集合存储元素时,内部的HashMap会给这个数据绑定一Object类型的常量,并且该常量在HashSet中去取不到(这样就借助了HashMap集合实现了HashSet的集合功能)

public boolean add(E e) {
         return map.put(e, PRESENT)==null;  //PRESENT就是一个object常量
      }
        //内部的成员常量
  private static final Object PRESENT = new Object();
 

总结:
HashSet的集合对象在调用add(“a”) ----->HashMap集合对象put(“a”,new Object)

hashCode()方法详解

此方法是我们Object提供的本地方法
什么本地方法

native关键字修饰的方法称之为本地方法,类似于接口,不过接口的方法都是用abstract修饰的,不同在于我们本地方法虽然也看不见方法体(实现体),但是实际上是jvm在加载时调用底层实现的。本地方法的实现体不是由java代码写的而可能由别的语言去写,如C语言,(可以这么理解,连接java代码和其他语言实现的代码入口)

  • 通俗点就是:我们Java去实现本地的文件读写功能,实际上调用windows系统锁实现的功能,通过本地方法去调用C语言或C++代码

public native int hashCode();

分析:该方法的作用返回该对象的哈希码值,其实就是一个int类型数据

native修饰的此方法,我们就可以理解为根据系统资源计算一个哈希值(或者我们可以理解通过地址值来计算)

此方法支持为了提高哈希表的性能

public static void main(String[] args) {


        Object object1 = new Object();

        int h1 = object1.hashCode();
        System.out.println(h1);//1163157884  十进制

        int h2=object1.hashCode();
        System.out.println(h2);//1163157884

        /*
        * 注意:
        *   1.同一个对象多次调用hashCode()方法,返回给我们的系统计算的哈希码值是相同的
        * */



        Object object2 =new Object();
        int  h3=object2.hashCode();
        System.out.println(h3);//1956725890

        /*
        * 注意点2:
        *   Object类的hashCode方法,根据系统资源计算出的哈希码值(就是一个int类型数字),只要new对象,哈希值就是不同的
        *
        * */


        /*
        * toString():返回给我们地址值
        *
        * 所以这个方法我们从本质就可以看作返回给我们的哈希值
        *
        * public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());

                    此方法先拿hashCode的值,然后转换为16进制的字符串
            }
        *
        * */

        System.out.println(object1.toString());//java.lang.Object@4554617c   4554617c
        System.out.println(object2.toString());//java.lang.Object@74a14482   74a14482


        /*
        * 注意3.
        *   如果自定义类没有覆盖Object类的hasCode(),默认调用Object类的hasCode方法
        *       根据系统资源计算一个哈希值(int 数字),只要new对象,哈希值不同
        * */
    }
public static void main(String[] args) {





        //定义char的字符数组
        char[]  chars={'a','b','c'};


        //然后将字符数组转换为字符串
        //通过构造方法实现,还有其他方法
        String s1 =new String(chars);
        String s2  =new String(chars);


        //获取hashCode值
        int h4 = s1.hashCode();
        int h5 =s2.hashCode();
        System.out.println(h4);//96354
        System.out.println(h5);//96354


        /*
        *
        *因为s1和s2通过new方式,创建的,但是发现h4和h5的哈希码值相同,因为String类自己重写覆盖了
        * Object类中的hasCode方法
        *
        * 4.根据对String类的哈希值的测试,发现String重写覆盖了Object类中的hashCode()方法
         *   这个hashCode的值通过根据字符串的字符的ASCII码值按照一定算法,计算一个哈希值
         *   既然是算法就存在安全隐患
         *
         *   安全隐患:
         *      假设:字符串的哈希值,根据字符串中每个字符的阿斯克码值,简单相加而来
         *      "abc":  97+98+99=294
         *      "cab":  99+97+98=294
         *
         *
         *   总结:
         *      问题1
         *          哈希值不同,能否说明两个字符串内容一定不同?
         *              必须的,一定,绝对不同
         *
         *       问题2:
         *          哈希值相同,能否说明两个字符串的内容一定相同?
         *
         *              不能说明
         *                  继续调用equals方法
         *                  如果equals()返回:true说明两个字符串内容相同
         *                  如果返回fasle,则说明字符串内容不同
        *
        * */

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

 /*   String 的哈希值计算
 *      成员变量
 *      private int hash; // Default to 0  创建string变量,这个hash=0
 *   public int hashCode() {
 *
 *         int h = hash;   //默认是0;
 *
 *         //这个value就是{'a','b','c'}
 *         if (h == 0 && value.length > 0) {
 *             char val[] = value;
 *
 *             for (int i = 0; i < value.length; i++) {
 *                 h = 31 * h + val[i];  //val[i]字符,对应的阿斯克码值
 *             }
 *             hash = h;
 *         }
 *         return h;
 *     }
 *
 *
 *     计算hashCode值得算法:h = 31 * h + val[i];
 *     第一次循环:h=31*0+97=97;
 *     第二次循环:h=31*97+98=3105;
 *     第三次循环:h=31*3105+99=96354
 */
public class Test3 {


    public static void main(String[] args) {

        //定义char的字符数组
        char[]  chars={'a','b','c'};

        String s1 =new String(chars);

        int h4 = s1.hashCode();
        System.out.println(h4);
    }

在这里插入图片描述

public class Test2 {




        public static void main(String[] args) {


            HashSet hashSet =new HashSet();
            hashSet.add("abc");//96354
            hashSet.add("重地");//1179395
            hashSet.add("通话");//1179395
            hashSet.add("abc");
            System.out.println(hashSet);

            //abc的索引位置
            System.out.println(96354%16);
            System.out.println(1179395%16);
        }
    }

在这里插入图片描述

在这里插入图片描述

hashSet.add(“abc”)
add方法会调用"abc".hashCode方法计算哈希值(元素在集合中的存储位置)哈希值96354,然后在集合中找有没有96354哈希值的元素,发现没有,就会把 "abc"存储到集合中。
96354%16=2

hashSet.add(“重地”)
add方法会调用"adb".hashCode方法计算哈希值(元素在集合中的存储位置)哈希1179395,然后在集合中找有没有1179395哈希值的元素,发现没有,就会把"重地"存储到集合中。
1179395%16=3

hashSet.add(“通话”)
add方法会调用"通话".hashCode方法计算哈希值(元素在集合中的存储位置)哈希1179395,然后在集合中找有1179395哈希值的元素,返现有,就会调用"通话".equals()和当前哈希值相同的元素,进行比较,返回false,把"通话"存储到集合中,并且挂在"重地"元素下面
*1179395%16=3

hashSet.add(“abc”);
add方法会调用"abc".hashCode方法计算哈希值(元素在集合中的存储位置)
哈希值96354,然后在集合中找有没有96354哈希值的元素,发现有,接着调用 “abc”.equals(),与哈希值相等的元素做比较,这时返回的结果true,则证明内容相同不给存储


存储自定义对象,并且保证元素的唯一性

package Test1;

/**
 * @作者:Xem626
 * @date: 2022/7/20 20:03
 * @TODO
 */
public class Student {


        private String name;

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


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

        public Student() {
        }
  }


package Test1;

import java.util.HashSet;

/**
 * @作者:Xem626
 * @date: 2022/7/20 20:03
 * @TODO
 */
public class Test3 {

    public static void main(String[] args) {

        HashSet<Student> students = new HashSet<>();



        //创建学生
        Student student1=new Student("柳岩",28);
        Student student2=new Student("林青霞",28);
        Student student3=new Student("王祖贤",28);
        Student student4=new Student("林青霞",28);
        Student student5=new Student("范冰冰",28);
        Student student6=new Student("林青霞",43);


        //添加元素

        students.add(student1);
        students.add(student2);
        students.add(student3);
        students.add(student4);
        students.add(student5);
        students.add(student6);

        for (Student student :students){

            System.out.println(student.getName()+"-----------"+student.getAge());
        }
    }
}

在这里插入图片描述

这里有重复
目前是不符合我的要求的:因为我们知道HashSet底层依赖的是hashCode()和equals()方法。

而这两个方法我们在学生类中没有重写,所以,默认使用的是Object类。

这个时候,他们的哈希值是不会一样的,根本就就不会继续判断,执行了添加操作。

要在自定义存储的类中覆写,hashCode()和equals(),可以解决

 @Override
    public boolean equals(Object object){

        if (this==object) {
            return true;
        }
        if (object==null) {
            return false;
        }
        if (this.getClass()!=object.getClass()) {
            return false;
        }
        Student student = (Student) object;
        if (age!=student.age) {
            return false;
        }

        if (name==null) {
            if (student.name!=null) {
                return false;
            }
        }else if (!name.equals(student.name)) {
            return false;
        }
        System.out.println("this.name"+this.name+"----"+"student"+student.name);
        return true;

自动生成:

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
2.使用HashSet类动态存储数据

加入现在需要很多数据总查找某个数据,LinkedList类就无需考虑了,它的数据结构决定了它的查找效率低下。如果使用ArrayList类,在部知道数据的索引且需要全部遍历的情况下,效率一样很低下。为此Java集合框架提供了一个查找效率高的集合类HashSet。HashSet类实现了Set接口,是使用Set集合时最常用的一个实现类。HashSet。HashSet类实现了Set接口,是使用Set集合时最常用的一个实现类。

HashSet集合的特点:

​ 1).集合内的元素是无序排列

​ 2).HashSet类是非线程安全

​ 3)允许集合元素为null。

HashSet类的常用方法

方法说明
boolean add(Object o)如果Set中尚未包含指定元素o,则添加指定元素o
void clear()从set中移除所有的元素
int size()返回Set中的元素的数量(Set的容量)
boolean isEmpty()如果Set不包含任何元素,则返回true
boolean contains(Object o)如果Set包含指定元素o,则返回true
boolean remove(Object o)如果指定元素o存在于set中,则将移除

例4
需求:使用HashSet类的常用方法存储操作新闻标题信息,并返回遍历集合

​ 实现步骤:

​ (1).创建HashSet对象,并添加数据

​ (2).获取新闻标题的总数

​ (3).判断集合中是否包含汽车新闻标题

​ (4).移除对象

​ (5).遍历集合

​ (6).判断集合是否为空

package Test1;

import java.util.HashSet;

/**
 * @作者:Xem626
 * @date: 2022/7/12 21:17
 * @TODO
 */
public class Test4 {
    public static void main(String[] args) {

        //创建HashSet集合对象
        HashSet hashSet=new HashSet();

        NewTitle newTitle=new NewTitle(1,"汽车","管理员");
        NewTitle newTitle1=new NewTitle(2,"高考","管理员");

        hashSet.add(newTitle);
        hashSet.add(newTitle1);

        System.out.println("获取新闻标题总数:"+hashSet.size());

        //判断是否含有汽车标题
        System.out.println("汽车标题的新闻是否存在:"+hashSet.contains(newTitle));

        //移除对象
        hashSet.remove(newTitle1);
        System.out.println("高考标题是否存在:"+hashSet.contains(newTitle1));

        for(Object o:hashSet){
            NewTitle title=(NewTitle) o;
            System.out.println(title.getName());
        }
        System.out.println("-------------------------");

        hashSet.clear();
        for(Object o1:hashSet){
            NewTitle title=(NewTitle) o1;
            System.out.println(title.getName());
        }

        NewTitle newTitle2=new NewTitle(1,"汽车","管理员");
        NewTitle newTitle3=new NewTitle(2,"高考","管理员");

        hashSet.add(newTitle2);
        hashSet.add(newTitle3);
        //判断集合是不是空
        boolean empty = hashSet.isEmpty();
        System.out.println(empty);

    }
}

在这里插入图片描述

3.LinkedHashSet 是HashSet子类

和hashSet方法基本一样,唯一区别存取取出有序

1.底层的数据结构:
数组+双向链表/红黑树(当链表节点数>8同时数组元素>=64时,会把链表变成红黑树)
2.查询速度快,增删速度也不慢
3.无索引:指的是不能通过索引来获得元素
4.元素不可重复:依赖所属类的hashCode()和equals()方法
5.线程不安全,不同同步,但是效率高
6有序的:怎么存就是怎么取(因为加了一个链表)

public class Test4 {


        public static void main(String[] args) {


            //创建一个集合对象
            LinkedHashSet linkedHashSet =new LinkedHashSet();
            linkedHashSet.add("hello");
            linkedHashSet.add("java");
            linkedHashSet.add("world");


            for (Object o:linkedHashSet){

                String s= (String) o;
                System.out.println(s);
            }
        }


    }

在这里插入图片描述

4.TreeSet

能够按照对元素某种顺序排列
排列顺序有两种方式

  • 1.自然排序
  • 2.比较器排序
  • Tree集合的特点:排序和唯一
public class Test {


    public static void main(String[] args) {


        TreeSet treeSet =new TreeSet();


        //添加元素
        treeSet.add(20);
        treeSet.add(18);
        treeSet.add(23);
        treeSet.add(22);
        treeSet.add(17);
        treeSet.add(24);
        treeSet.add(19);
        treeSet.add(18);
        treeSet.add(24);

        for (Object o:treeSet){

            int i = (int) o;
            System.out.println(o);
        }
    }



}

在这里插入图片描述
TreeSet存储自定义对象时,要保证排序和唯一

  • 1.没有告诉怎么去排序 自然,按照年龄大小来排序
  • 2.元素什么情况,唯一我也没告诉它成员变量值相同就是同一元素
package Test1;

import java.util.Objects;

/**
 * @作者:Xem626
 * @date: 2022/7/20 20:03
 * @TODO
 */
public class Student implements Comparable<Student> {

    private String name;

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


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

    public Student() {
    }


    @Override
    public int compareTo(Student o) {

        // 主要条件 姓名的长度
        int num = this.name.length() - o.name.length();
        // 姓名的长度相同,不代表姓名的内容相同
        int num2 = num == 0 ? this.name.compareTo(o.name) : num;
        // 姓名的长度和内容相同,不代表年龄相同,所以还得继续判断年龄
        int num3 = num2 == 0 ? this.age - o.age : num2;
        return num3;
    }
}

package Test1;

import java.util.TreeSet;

/**
 * @作者:Xem626
 * @date: 2022/7/20 20:32
 * @TODO
 */
public class Test6 {



        public static void main(String[] args) {

            TreeSet treeSet =new TreeSet();


            //创建学生
            Student student1=new Student("lingqingxia",27);

            Student student2=new Student("zhangguorong",29);
            Student student3=new Student("wanglihong",23);
            Student student4=new Student("lingqingxia",27);
            Student student5=new Student("fanbingbing",22);
            Student student6=new Student("wuqilong",40);
            Student student7=new Student("fengqingy",22);



            treeSet.add(student1);
            treeSet.add(student2);
            treeSet.add(student3);
            treeSet.add(student4);
            treeSet.add(student5);
            treeSet.add(student6);
            treeSet.add(student7);



            //for遍历
            for (Object o :treeSet){

                Student student = (Student) o;
                System.out.println(student.getName()+"---"+student.getAge());
            }

        }

    }


在这里插入图片描述
compareTo重写:

@Override
    public int compareTo(Student o) {

        // 主要条件 姓名的长度
        int num = this.name.length() - o.name.length();
        // 姓名的长度相同,不代表姓名的内容相同
        int num2 = num == 0 ? this.name.compareTo(o.name) : num;
        // 姓名的长度和内容相同,不代表年龄相同,所以还得继续判断年龄
        int num3 = num2 == 0 ? this.age - o.age : num2;
        return num3;
    }

按照姓名的长度来排序
TreeSet集合保证元素排序和唯一性
唯一性:是根据比较的返回是否是0来决定
排序:
1.自然排序(元素具备比较性)

让元素所属类实现自然排序接口Comparable
2.比较器排序

让集合的构造方法接收一个比较器接口的子类对象

TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //姓名长度
                int num =s1.getName().length()-s2.getName().length();

                //姓名内容
                int num2=num==0?s1.getName().compareTo(s2.getName()):num;

                //年龄
                int num3 =num2==0?s1.getAge()-s2.getAge():num2;
                return num3;
            }
        });


编写一个程序,获取10个1-20的随机数

分析:
1.创建随机对象
2创建HashSet集合
3.判断集合的长度是不是小于10
是:就继续创建一个随机数添加
否,不用放
4.遍历

public class Test1 {

    public static void main(String[] args) {


        Random random =new Random();


        //创建集合对象
        HashSet hashSet =new HashSet();
        while (hashSet.size()<10){

            int num =random.nextInt(20)+1;
            hashSet.add(num);
        }


        for (Object o:hashSet){

            int i = (int) o;
            System.out.println(i);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xem626

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值