zoukankan      html  css  js  c++  java
  • java 集合 Se HashTreeSet

    Set是Collection的子接口,与List相对 
    Set集合中的元素的特点是1,无序性 2,无下标3,无重复的元素 
    Set是个接口,所以无法直接创建对象,要依赖它的实现类来创建对象 
    Set的实现类有两个,一个是HashSet,另一个是TreeSet

        Set<String> set = new HashSet<>();
        set.add("c");
        set.add("b");
        set.add("d");
        set.add("a");
        set.add("a");
        System.out.println(set);

    输出的结果是

    [a, b, c, d]//根据输出结果,我们可以看出set 的无序性和无重复性

    Set的成员方法基本上都是继承了collection方法 
    下面我们看一下Set的实现类

    HashSet 
    HashSet是set的实现类,底层是由哈希算法实现的 
    因为HashSet中能会添加相同的对象,所以HashSet常常被用来去除集合中重复的元素

        HashSet<String> hashSet = new HashSet<>();
        hashSet.add("a");
        hashSet.add("b");
        hashSet.add("d");
        hashSet.add("d");
        System.out.println(hashSet);

    输出结果是:

    [a, b, d]

    根据输出的结果可以看出HashSet确实去除了重复的对象,那么add的底层实现具体是什么? 
    因为HashSet是由哈希算法来实现的,在一个对象要被添加到集合中的时候,会被系统分配一个哈希值,系统首先会根据这个对象的哈希值去和集合中的对象去比较,如果哈希值不同就可以加入,如果不同,就去比较俩字符串的值是否一样,如果一样就不能加入,因此把重复的对象是无法加入HashSet的

    当我们加入的对象是数据类型不是系统的数据类型,我们来看一下HashSet如何去除重复的 
    先写一个StudentL类,student有俩变量,一个是名字.一个是年龄

    public class Student 
    {
      private String name;
      private int age;
    public Student(String name, int age) {
        super();
        this.name = name;
        this.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;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    } 
    }

    下面我们在HashSet集合中添加三个Student类的对象

      HashSet<Student> hashSet = new HashSet<>();
       hashSet.add(new Student("张三", 18));
       hashSet.add(new Student("李四", age));
       hashSet.add(new Student("张三", 18));
       System.out.println(hashSet);

    输出的结果是:

    [Student [name=张三, age=18], Student [name=张三, age=18], Student [name=李四, age=19]]

    可以看出,俩张三都被添加进去了,但是重复的我们并不想添加进去,为什么会被添加进去呢 
    这时因为在创建student的时候,我们给每个对象都在堆内存中开辟了一片空间,虽然俩对象中的元素相等,但是他们的地址是不一样的,所以系统认为他们是不同的,所以被添加进去了 
    如果我们要让系统把student中重复的去掉,我们只能从底层去操作,因为HashSet是系统内部根据比较Hash值来判断的,然后在调用equals方法来比较的,所以我们要重写Object类中的hashCode和equals方法 
    因为我们要对Student类的对象排序,自然是Student类的对象来调用,所以在Student类中进行方法的重写

     //重写hashCode方法
     @Override
        public int hashCode() 
    //这里返回的是要添加的对象的哈希值
        {
            return 1;
        }
    //重写equals方法
     @Override
        public boolean equals(Object obj) 
     {
         Student student = (Student)obj;
         if (student.name.equals(this.name)&&student.age == this.age)
         {
            return true;
         }
            return false;
        }
    }

    我们再次运行,输出的结果是:

    [Student [name=张三, age=18], Student [name=李四, age=19]]

    这次可以看出重复的对象已经被去除了.

    TreeSet 
    TreeSet是Set的一个实现类,底层是由二叉树实现的 
    下面我们创建一个TreeSet类型的集合,并将几个整数装进去

    TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(10);
        treeSet.add(8);
        treeSet.add(15);
        treeSet.add(11);
        treeSet.add(8);
        System.out.println(treeSet);

    输出的结果是

    [8, 10, 11, 15]//我们可以根据输出的结果看出TreeSet会将对象排序并将重复的删除了

    HashSet的作用就是去除重复的对象,而TreeSet的主要作用就是排序 
    下面我们在TreeSet中添加非系统类的对象 
    例:在TreeSet中添加四个学生类对象

           TreeSet<Student> treeSet = new TreeSet<>();
           treeSet.add(new Student("张三", 18));
           treeSet.add(new Student("李四", 19));
           treeSet.add(new Student("张三", 18));
           System.out.println(treeSet); 

    当我们运行的时候,会发现系统会报错,这是因为我们添加进去的Student类,不是系统类,系统不知道怎么排序,所以会报错 
    我们写系统类之所以系统会排序,是因为系统类中都实现了Comparable接口中的比较的抽象方法,系统在add的时候,要添加的系统对象会调用这个比较方法,进行排序和去除重复的对象 
    所以我们需要在Student类中实现接口,重写接口中的比较方法

    //重写comparable接口中的抽象方法,主要是把对象的排序的规则写进去
    @Override
    //要注意的是当返回值为0的时候,系统就会认为俩值是一样的,就不会添加该对象
        public int compareTo(Student o) {
            // TODO Auto-generated method stub
        int ageNum = this.age - o.age;
        int nameNum = this.name.compareTo(o.name);
        //当年龄一样的时候,我们就比较俩者的姓名
        return ageNum == 0 ? nameNum :ageNum;
        }

    运行得到的结果是:

    [Student [name=张三, age=18], Student [name=李四, age=19]]

    可以看出我们的运行结果已经根据年龄排序了,但是这种方法有一个局限,如果该类的对象被装到俩集合中,排序的条件不同,这样的话就没法重写抽象方法了.

    这时候我们可以创建每一个集合创建一个比较器,用比较器来排序 
    我们这里要用到接口Comparator中的比较方法,我们需要给Comparator接口写一个实现类,在类中重写抽象方法 compare(),方法中也是用来写排序的规则的

    public class StudentImpl implements Comparator<Student> {
     @Override
    public int compare(Student o1,Student o2) 
     {
       int intNum = o1.getAge() - o2.getAge();
       int nameNum = o1.getName().compareTo(o2.getName());
       return nameNum == 0 ? intNum :nameNum;
    }
    }

    在实现了comparator接口后,我们根据其实现类来为TreeSet创建一个比较器

        TreeSet<Student> treeSet = new TreeSet<>(new StudentImpl());
           treeSet.add(new Student("张三", 18));
           treeSet.add(new Student("李四", 19));
           treeSet.add(new Student("张三", 18));
           System.out.println(treeSet);

    这样输出的结果是:

    [Student [name=张三, age=18], Student [name=李四, age=19]]

    这样即使student类被装到很多个集合中,并且排序的条件不同,我们也可以为每一个集合创建一个特有的比较器来比较

  • 相关阅读:
    大端序与小端序
    中断分类
    PHP开发框架[国内框架]
    PHP开发框架[流行度排名]
    ecshop 后台分页功能
    Windows下phpStudy中的Apache无法启动的排查方法
    Windows里配置Apache2.2+PHP5.3+mod_fcgid运行高效的FastCGI模式
    Apache多虚拟主机多版本PHP(5.2+5.3+5.4)共存运行配置全过程
    让 Node.js 支持 ES6 的语法
    微信小程序请求wx.request数据,渲染到页面
  • 原文地址:https://www.cnblogs.com/jtlgb/p/9518408.html
Copyright © 2011-2022 走看看