前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java集合Set接口详解——含源码分析

Java集合Set接口详解——含源码分析

作者头像
秋名山码神
发布2022-12-13 15:19:36
2600
发布2022-12-13 15:19:36
举报
文章被收录于专栏:码神随笔

文章目录


前言

前几天我们看了Java集合List接口详解——含源码分析,今天我们看一个数据一个数据存储的另一个接口——Set。

Set

前面我们说List最大的特点是:有序,不唯一,而set是:无序,唯一,无序不是随机,我们后面来通过实现类来看他这俩个特点,还有与list差距比较大的地方是set没有与索引相关的方法,在ArrayList中我们说过,没有索引,就不能用普通for循环来进行遍历。

HashSet

代码语言:javascript
复制
import java.util.HashSet;

public  class text1 {
    public static void main(String[] args) {
        HashSet<Integer> hashSet = new HashSet<>();
        hashSet.add(1);
        hashSet.add(2);
        hashSet.add(2);//唯一性的体现

        hashSet.add(3);
        hashSet.add(4);
        System.out.println(hashSet);
    }
}

很好的体现了唯一性,但是有小伙伴可能要说,这不是有序的吗?

其实这是hash内部的存储,与查找,在我的实验中,短数据,有序的Integer类型确实会产生这种情况(具体我们后文再说,hh),但是我们看下面的代码,即可知道hashset的无序性(存入,与取出的顺序不确定)。

代码语言:javascript
复制
import java.util.HashSet;

public  class text1 {
    public static void main(String[] args) {
        HashSet<String > hashSet = new HashSet<>();
        hashSet.add("aaa");
        hashSet.add("adbab");
        hashSet.add("abdb");
        hashSet.add("abw");
        System.out.println(hashSet);
    }
}

下面,我们来存取自定义数据类型,Student对象,存入他们的学号和姓名

代码语言:javascript
复制
import java.util.HashSet;

public  class text1 {
    public static class  Student{
        private int    id;
        private String name;

        public Student(int i, String s) {
            this.id = i;
            this.name = s;
        }

        public  void setId(int id){
            this.id = id;
        }
        public int getId(){
            return id;
        }
        public void setName(String name){
            this.name = name;
        }
        public String getName(){
            return name;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    public static void main(String[] args) {
        HashSet<Student> hashSet = new HashSet<>();
        hashSet.add(new Student(1,"ymm"));
        hashSet.add(new Student(1,"ymm"));
        hashSet.add(new Student(2,"yxc"));
        hashSet.add(new Student(3,"hh"));
        System.out.println(hashSet);
    }
}

hh,这块hashset又可以重复了?

hashset如何进行存取:

  1. 集合中存入数据
  2. 要访问的时候,调用对应的hashcode方法来计算哈希值
  3. 通过哈希值和一个**表达式(我们是不知道的)**计算所对应的值在数组中存放的位置

19便不会被放入。

最后要求:数组+链表=哈希表,重要的方法,也可以说是要满足hashset集合,就要重写hashcode和equals

重写hashcode和equals

代码语言:javascript
复制
		@Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Student)) return false;
            Student student = (Student) o;
            return getId() == student.getId() && getName().equals(student.getName());
        }

        @Override
        public int hashCode() {
            return Objects.hash(getId(), getName());
        }

LinkedHashSet

如果我们想要元素有序,可以使用LinkedHashSet,底层是一个链表,这个链表的作用就是将放入的元素串在一起,方便有序的遍历

特点:有序(按照输入顺序进行输出),唯一

代码语言:javascript
复制
		LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("aa");
        linkedHashSet.add("bbc");
        linkedHashSet.add("aabsb");
        linkedHashSet.add("sjfjdakl");
        System.out.println(linkedHashSet);

treeset

上面我们说hashset是无序(输入顺序和输出顺序不一致),linkedhashset(输入顺序和输出顺序一致),那么treeset就比较牛逼了,直接输出的时候就帮你排好序(底层是一个红黑树(特殊的二叉查找树)),但是在这个之前,我们先来看一下,比较器这个概念:

比较器

  1. 相同数据类型的比较 这个没什么好说的,我们直接来看代码:
代码语言:javascript
复制
	public int compareTo(String anotherString) {
        byte v1[] = value;
        byte v2[] = anotherString.value;
        if (coder() == anotherString.coder()) {
            return isLatin1() ? StringLatin1.compareTo(v1, v2)
                              : StringUTF16.compareTo(v1, v2);
        }
        return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
                          : StringUTF16.compareToLatin1(v1, v2);
     }

上述代码是String内部的比较方法,我们一起来读一下,首先是按照位数进行比较,如果位数相同按照字典序进行一个比较,还是比较简单的

  1. 比较自定义的数据类型 上面我们看到,相同的数据类型,基本上系统都集成了比较的接口和方法,那么我们自定义的数据类型怎么进行比较呢?是否可以像上面的一样,直接自己写一个比较方法,hh
代码语言:javascript
复制
//内部比较器
public class Student implements Comparable<Student> {
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    private int id;
    private String name;

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Student s1 = new Student(1,"ymm");
        Student s2 = new Student(2,"yxc");
        System.out.println(s1.compareTo(s2));

    }

    //对方法进行重写
    @Override
    public int compareTo(Student o) {

        //按照id进行比较
//        return this.getId() - o.getId();

        //按照姓名进行比较
        return this.getName().compareTo(o.getName());
    }
}

缺点:要想比较id就要注释姓名,要想比较姓名就要注释id,故引出下面的外部比较器

代码语言:javascript
复制
class BiJiao01 implements Comparator<Student> {
    @Override
    public int compare(Student o1,Student o2){
        return o1.getName().compareTo(o2.getName());
    }
}
class BiJiao02 implements Comparator<Student>{
    @Override
    public int compare(Student o1,Student o2){
        return o1.getId() - o2.getId();
    }
}


class BiJiao03 implements Comparator<Student>{
    @Override
    public int compare(Student o1,Student o2){
        if((o1.getId() - o2.getId()) == 0){
            return ((o1.getName()).compareTo(o2.getName()));
        }else{
            return o1.getId() - o2.getId();
        }
    }
}

由于外部比较器利用了多态,所以可拓展性比较好。

TreeSet特点:唯一,无序(没有按照输入顺序进行输出),有序(按照升序进行遍历)

特殊的二叉搜索树(后面有时间写一篇):

不难看出,在树中放入数据的时候,最重要的事情就是比较,比较的实现: = 0 <0 >0

代码语言:javascript
复制
import java.util.TreeSet;

public class treeSetDemo {
    public static void main(String[] args) {
        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(12);
        treeSet.add(3);
        treeSet.add(7);
        System.out.println(treeSet);
    }
}

底层运用的是Integer内部比较器

自定义比较器来实现Student类的treeSet存取

代码语言:javascript
复制
import java.util.Comparator;

public class Student implements Comparable<Student> {
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    private int id;
    private String name;

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //对方法进行重写
    @Override
    public int compareTo(Student o) {

        //按照id进行比较
        return this.getId() - o.getId();

        //按照姓名进行比较
        //return this.getName().compareTo(o.getName());
    }
}



import java.util.TreeSet;
public class treeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> treeSet1 = new TreeSet<>();
        treeSet1.add(new Student(1,"ymm"));
        treeSet1.add(new Student(3,"sss"));
        treeSet1.add(new Student(2,"jjj"));
        System.out.println(treeSet1);
    }
}

外部比较器同理,hh,就像我上面比较器那样,但是在创建类的时候有一点点不一样

代码语言:javascript
复制
public static void main(String[] args){
	Comparator<Student> com = new BiJiao();
	TreeSet<Student> ts = new TreeSet<>(com);
	//指定了一个外部比较器
}

最后

这里挖一个坑,后面我们来讲一下二叉查找树,hh,挖坑没停止

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • Set
    • HashSet
      • LinkedHashSet
    • treeset
      • 比较器
  • 最后
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档