-集合(Set&Map)

今日内容

  • Set
    • HashSet
    • TreeSet
    • 相关数据结构
  • Map

==1. Set==

1.1 概述

Set集合是单列集合的另一个分支。单列集合中所有的共享特性,set都具有:

  • 作为容器可以存储元素,只能存储引用数据类型数据

  • 可以使用迭代器遍历

1.2 Set特点

  1. 唯一。元素不可重复,可以使用set实现去重。

  2. 无序。无法保证取出的顺序和存入的顺序一致。

  3. 没有索引。不能通过索引操作元素(通过索引获取、删除、修改等)

Set是一个接口,HashSet是一个他的实现类。

  • 演示代码

    package com.itheia;
    ​
    import java.util.HashSet;
    ​
    /**
     * Set通用特点演示
     */
    public class SetDemo01 {
        public static void main(String[] args) {
            // 创建set集合对象
            HashSet<String> set = new HashSet<>();
    ​
            // 添加元素
            set.add("bbb");
            set.add("aaa");
            set.add("ddd");
            set.add("ccc");
    ​
            // 验证是否无序:[aaa, ccc, bbb, ddd]
            System.out.println("set = " + set);
    ​
            // 验证是否不可重复(唯一)
            System.out.println("set.add(\"aaa\") = " + set.add("aaa"));
            System.out.println("set = " + set);
    ​
            // 验证无索引
            // set.get(1);
    ​
        }
    }

==1.3 HashSet==

1.3.1 概述

HashSet是单列集合Set常见的实现类之一。

底层数据结构是Hash表;但是JDK版本的不同,Hash表的实现原理略有差异:

  • JDK8以前:哈希表 = 数组+链表

  • JDK8及以后:哈希表 = 数组+链表+红黑树

其中的Hash值又称为散列值,是对象hashCode方法的返回值。hashCode方法定义在Object中,但是其值生成可能和内存有关,我们一般会重写hashCode,让其返回值和成员变量相关,且

  • 同一个对象,多次调用hashCode方法返回值应该一样

  • 不同的对象,其hashCode返回值应该尽量不一样

但是又无法保证不同对象的hash值必然不同,如果出现相同的情况,称为hash冲突。

Hash表性能比较均衡,增删改查都比较快;但是不适合范围查询。

1.3.2 数据结构

我们发现往HashSet集合中存储元素时,底层调用了元素的两个方法:一个是hashCode方法获取元素的hashCode值(哈希值);另一个是调用了元素的equals方法,用来比较新添加的元素和集合中已有的元素是否相同。

  • 只有新添加元素的hashCode值和集合中以后元素的hashCode值相同、新添加的元素调用equals方法和集合中已有元素比较结果为true, 才认为元素重复。

  • 如果hashCode值相同,equals比较不同,则以链表的形式连接在数组的同一个索引为位置(如上图所示)

在JDK8开始后,为了提高性能,当链表的长度超过8时,就会把链表转换为红黑树,如下图所示:

相关概念:

  • Hash值:散列值,hashCode方法返回值;

  • 同一个对象,多次调用hashCode方法的返回值应该一样

  • 不同的对象的hash值尽量应该不一样。

  • 自定义类一般会重写hashCode方法,让该方法的返回值与成员变量相关,且尽量与其他对象的hashCode返回值不同。

1.3.3 HashSet去重原理

原理分析:

  • 一个元素想要存入Hash表中,需要依赖两个方法:hashCode、equals

  • HashSet集合,想要实现元素不重复,依赖的也是这两个方法

  • 使用HashSet存储自定义对象Student(name,age,height)

演示代码:

  • 实体类Student.java

    package com.itheia;
    ​
    /**
     * HashSet去重原理演示:自定义实体类
     */
    public class Student {
        private String name;
        private int age;
        private double height;
    ​
    ​
        public Student() {
        }
    ​
        public Student(String name, int age, double height) {
            this.name = name;
            this.age = age;
            this.height = height;
        }
    ​
        // getter & setter 自己编写
    ​
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", height=" + height +
                    '}';
        }
    ​
        /**
         * 比较的是属性值。如果属性值都一样,则认为是同一个对象,返回true
         * @param o
         * @return
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    ​
            Student student = (Student) o;
    ​
            if (age != student.age) return false;
            if (Double.compare(student.height, height) != 0) return false;
            return name != null ? name.equals(student.name) : student.name == null;
        }
    ​
        /**
         * 1. hash值和成员变量的值相关,只要变量值不变,对象的hash值有不会变化
         * 2. 通过相对复杂的计算逻辑,尽量实现不同对象的hash值不同
         * @return
         */
        @Override
        public int hashCode() {
            int result;
            long temp;
            result = name != null ? name.hashCode() : 0;
            result = 31 * result + age;
            temp = Double.doubleToLongBits(height);
            result = 31 * result + (int) (temp ^ (temp >>> 32));
            return result;
        }
    }
  • 测试类SetDemo02.java

    package com.itheia;
    ​
    import java.util.HashSet;
    ​
    /**
     * HashSet去重原理演示:测试类
     */
    public class SetDemo02 {
        public static void main(String[] args) {
            // 创建set集合对象
            HashSet<Student> stus = new HashSet<>();
    ​
            // 创建元素对象并添加进集合
            stus.add(new Student("Ikun",18,188.0));
            stus.add(new Student("华强",19,189.0));
            stus.add(new Student("芳芳",88,158.0));
            stus.add(new Student("Ikun",18,188.0));
    ​
            // 查询集合中元素,确认去重效果
            System.out.println("stus = " + stus);
        }
    }
  • 打印结果如下:实现了去重

    stus = [Student{name='芳芳', age=88, height=158.0}, Student{name='华强', age=19, height=189.0}, Student{name='Ikun', age=18, height=188.0}]

1.4 LinkedHashSet

常用于去重。

1.4.1 概述

LinkedHashSet底层采用的是也是哈希表结构,只不过额外新增了一个双向链表来维护元素的存取顺序。

特点:有序、唯一、无索引。

底层基于哈希表,使用链表记录添加顺序。

1.4.2 数据结构

如下下图所示:

总结:

  • 使用HashSet存入自定义对象时,对象所属的类要重写hashCode和equals方法。

每次添加元素,就和上一个元素用双向链表连接一下。第一个添加的元素是双向链表的头节点,最后一个添加的元素是双向链表的尾节点。

1.4.3 演示代码

  • 实体Student类

    <span style="background-color:#f8f8f8"><span style="color:#aa5500">// 与HashSet中一致</span></span>
  • 测试类

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheia</span>;
    ​
    <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">LinkedHashSet</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* LinkedHashSet演示</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo03</span> {
        <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
            <span style="color:#aa5500">// 创建集合对象LinkedHashSet</span>
            <span style="color:#000000">LinkedHashSet</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span> <span style="color:#000000">stus</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">LinkedHashSet</span><span style="color:#981a1a"><></span>();
    ​
            <span style="color:#aa5500">// 添加元素</span>
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>,<span style="color:#116644">18</span>,<span style="color:#116644">188.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"华强"</span>,<span style="color:#116644">19</span>,<span style="color:#116644">189.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"芳芳"</span>,<span style="color:#116644">88</span>,<span style="color:#116644">158.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>,<span style="color:#116644">18</span>,<span style="color:#116644">188.0</span>));
    ​
            <span style="color:#aa5500">// 遍历集合查询元素顺序</span>
            <span style="color:#770088">for</span> (<span style="color:#000000">Student</span> <span style="color:#000000">stu</span> : <span style="color:#000000">stus</span>) {
                <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"stu = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">stu</span>);
            }
    ​
        }
    }</span>
  • 控制台输出结果:证明有序唯一

    <span style="background-color:#f8f8f8"><span style="color:#000000">stu</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Student</span>{<span style="color:#000000">name</span><span style="color:#981a1a">=</span><span style="color:#aa1111">'Ikun'</span>, <span style="color:#000000">age</span><span style="color:#981a1a">=</span><span style="color:#116644">18</span>, <span style="color:#000000">height</span><span style="color:#981a1a">=</span><span style="color:#116644">188.0</span>}
    <span style="color:#000000">stu</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Student</span>{<span style="color:#000000">name</span><span style="color:#981a1a">=</span><span style="color:#aa1111">'华强'</span>, <span style="color:#000000">age</span><span style="color:#981a1a">=</span><span style="color:#116644">19</span>, <span style="color:#000000">height</span><span style="color:#981a1a">=</span><span style="color:#116644">189.0</span>}
    <span style="color:#000000">stu</span> <span style="color:#981a1a">=</span> <span style="color:#000000">Student</span>{<span style="color:#000000">name</span><span style="color:#981a1a">=</span><span style="color:#aa1111">'芳芳'</span>, <span style="color:#000000">age</span><span style="color:#981a1a">=</span><span style="color:#116644">88</span>, <span style="color:#000000">height</span><span style="color:#981a1a">=</span><span style="color:#116644">158.0</span>}</span>

1.5 TreeSet

使用频率不高,理解为主。

1.5.1 概述

  • 元素不可重复,可以使用TreeSet实现去重。

  • 没有索引。不能通过索引操作元素(通过索引获取、删除、修改等)

  • 无法保证存取顺序一致。但是可以将元素按照指定规则进行排序

    • 自然排序(TreeSet集合默认的排序方式)

    • 比较器排序

1.5.2 存储JDK系统类对象

需求:

  • 使用TreeSet存储Integer类型数据

  • 使用TreeSet存储String类型数据

演示代码1:存储Integer

  • SetDemo04.java

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheia</span>;
    ​
    <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">TreeSet</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* TreeSet存储Integer类型元素</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo04</span> {
        <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
            <span style="color:#aa5500">// 创建集合对象,指定泛型为Integer</span>
            <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><</span><span style="color:#008855">Integer</span><span style="color:#981a1a">></span> <span style="color:#000000">nums</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><></span>();
    ​
            <span style="color:#aa5500">// 添加元素</span>
            <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">5</span>);
            <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">1</span>);
            <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">8</span>);
            <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">3</span>);
            <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">6</span>);
            <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">9</span>);
    ​
    ​
            <span style="color:#aa5500">// 打印集合查看元素顺序:验证无序(排序)</span>
            <span style="color:#aa5500">// nums = [1, 3, 5, 6, 8, 9]</span>
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"nums = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">nums</span>);
    ​
    ​
            <span style="color:#aa5500">// 打印集合查看元素顺序:唯一</span>
            <span style="color:#000000">nums</span>.<span style="color:#000000">add</span>(<span style="color:#116644">9</span>);
            <span style="color:#aa5500">// nums = [1, 3, 5, 6, 8, 9]</span>
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"nums = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">nums</span>);
    ​
            <span style="color:#aa5500">// 使用索引获取集合元素,验证无索引</span>
            <span style="color:#aa5500">// nums.get(1);</span>
        }
    }</span>

演示代码2:存储String

  • SetDemo04.java

    <span style="background-color:#f8f8f8">package com.itheima;
    
    import java.util.TreeSet;
    
    /**
     * TreeSet存储String类型元素
     */
    public class SetDemo05 {
        public static void main(String[] args) {
            // 创建集合对象,指定泛型为String
            TreeSet<String> strs = new TreeSet<>();
    
            // 添加元素
            strs.add("abc");
            strs.add("aac");
            strs.add("dfg");
            strs.add("bba");
            strs.add("bag");
            strs.add("ccc");
    
    
            // 打印集合查看元素顺序:验证无序(排序)。字典顺序,自然排序,按照升序排序。
            System.out.println("strs = " + strs);
    
    
            // 打印集合查看元素顺序:唯一
            strs.add("ccc");
            System.out.println("strs = " + strs);
    
            // 使用索引获取集合元素,验证无索引
            // strs.get(1);
        }
    }</span>

1.5.3 TreeSet排序-自然排序

  • 自然排序

    元素所属类型实现自然排序接口Comparable并重写compareTo方法后,创建该类对象并存入TreeSet集合,集合会自动调用元素类的compareTo方法,决定元素在集合中的顺序。此为自然排序。

自然排序要取元素类实现Comparable接口,需要做到以下几点:

  • 使用空参构造创建TreeSet集合

  • 元素类(eg:Student)需要实现Comparable接口

  • 重写元素类(eg:Student)中的compareTo方法

compareTo方法返回值与排序结果的关系

  1. 如果返回值为负数,表示当前存入的元素是较小值,存左边

  2. 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存

  3. 如果返回值为正数,表示当前存入的元素是较大值,存右边

  • 自然排序演示代码:以年龄为条件排序

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 让Student类实现Comparable接口,重写compareTo方法,就能实现存入TreeSet后自动排序,这就是自然排序。</span>
     <span style="color:#aa5500">* 实现接口的时候Comparable的泛型写当前类Student即可</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">Student</span> <span style="color:#770088">implements</span> <span style="color:#000000">Comparable</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span>{
        <span style="color:#aa5500">// 其他代码不变,省略不写。</span>
    ​
        <span style="color:#555555">@Override</span>
        <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">compareTo</span>(<span style="color:#000000">Student</span> <span style="color:#000000">o</span>) {
            <span style="color:#aa5500">//按照对象的年龄进行排序</span>
            <span style="color:#008855">int</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">-</span> <span style="color:#000000">o</span>.<span style="color:#000000">age</span>;
            <span style="color:#770088">return</span> <span style="color:#000000">result</span>;
        }
    }</span>

  • 测试类代码不变

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts</span>;
    ​
    <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">Arrays</span>;
    <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">TreeSet</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* TreeSet存储自定义类型对象:Student</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo06</span> {
        <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
            <span style="color:#aa5500">// 创建集合对象,泛型为Student</span>
            <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span> <span style="color:#000000">stus</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><></span>();
    ​
            <span style="color:#aa5500">// 创建元素对象并添加</span>
            <span style="color:#aa5500">// TreeSet不能直接存储自定义对象,因为TreeSet集合不知道对元素进行怎样的排序</span>
            <span style="color:#aa5500">// ClassCastException: com.itheima.ts.Student cannot be cast to java.lang.Comparable</span>
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"华强"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">187.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"芳芳"</span>, <span style="color:#116644">88</span>, <span style="color:#116644">158.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>));
    ​
            <span style="color:#aa5500">// 遍历集合中元素,验证set特点</span>
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"stus = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">stus</span>);
    ​
        }
    }</span>

  • 自然排序演示代码:以年龄为主要条件,姓名、身高为辅助条件排序

    <span style="background-color:#f8f8f8"><span style="color:#aa5500">// 年龄是主要条件,姓名身高为辅助条件</span>
    <span style="color:#aa5500">// 如果年龄不同就可以直接排序,年龄相同时才需要借助姓名排序。</span>
    <span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">mytreeset</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* 让Student类实现Comparable接口,重写compareTo方法,就能实现存入TreeSet后自动排序,这就是自然排序。</span>
     <span style="color:#aa5500">* 实现接口的时候Comparable的泛型写当前类Student即可</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">Student</span> <span style="color:#770088">implements</span> <span style="color:#000000">Comparable</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span>{
        <span style="color:#aa5500">// 其他代码</span>
    ​
        <span style="color:#555555">@Override</span>
        <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">compareTo</span>(<span style="color:#000000">Student</span> <span style="color:#000000">stu</span>) {
            <span style="color:#aa5500">// 让该方法的返回值和成员变量相关 name age height</span>
            <span style="color:#008855">int</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#116644">0</span>;
            <span style="color:#aa5500">// 首先按照年龄比</span>
            <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">-</span> <span style="color:#000000">stu</span>.<span style="color:#000000">age</span>;
    ​
            <span style="color:#aa5500">// 年龄相同则必身高</span>
            <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">result</span> <span style="color:#981a1a">==</span> <span style="color:#116644">0</span> <span style="color:#981a1a">?</span> (<span style="color:#008855">int</span>) (<span style="color:#770088">this</span>.<span style="color:#000000">height</span> <span style="color:#981a1a">-</span> <span style="color:#000000">stu</span>.<span style="color:#000000">height</span>) : <span style="color:#000000">result</span>;
    ​
            <span style="color:#aa5500">// 身高也相同,比名称</span>
            <span style="color:#770088">return</span> <span style="color:#000000">result</span> <span style="color:#981a1a">==</span> <span style="color:#116644">0</span> <span style="color:#981a1a">?</span> <span style="color:#770088">this</span>.<span style="color:#000000">name</span>.<span style="color:#000000">compareTo</span>(<span style="color:#000000">stu</span>.<span style="color:#000000">name</span>) : <span style="color:#000000">result</span>;
        }
    }</span>

  • 字符串的CompareTo方法

    字符串String同样的实现了Comparable接口,并重写了CompareTo方法。

    方法内部实现的按照字典顺序排序

1.5.4 TreeSet排序-比较器排序

自然排序高度依赖元素所属类的compareTo方法,如果排序规则发生要改变,就需要修改元素类的代码,这种设计在经常变换排序规则的环境中不合理。

这时就可以使用比较器排序,解耦排序逻辑元素实体类

比较器排序Comparator的使用步骤:

  • 使用TreeSet的带参构造创建集合对象

  • 创建集合对象时,有参构造传递的参数为Comparator(比较器接口)的实现类对象,

  • 比较器实现类需要重写compare(To1,To2)方法

注意:

  • 使用比较器排序时,元素类不需要实现任何接口;而是自定义比较器类,并由比较器实现Comparator接口

优势:

  • 比较排序算法和具体的元素类解耦分离了。

演示代码

  • Student类,不需要实现任何接口

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts02</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* HashSet去重原理演示:自定义实体类。不需要实现任何接口</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">Student</span>  {
        <span style="color:#770088">private</span> <span style="color:#008855">String</span> <span style="color:#000000">name</span>;
        <span style="color:#770088">private</span> <span style="color:#008855">int</span> <span style="color:#000000">age</span>;
        <span style="color:#770088">private</span> <span style="color:#008855">double</span> <span style="color:#000000">height</span>;
    ​
    ​
        <span style="color:#770088">public</span> <span style="color:#000000">Student</span>() {
        }
    ​
        <span style="color:#770088">public</span> <span style="color:#000000">Student</span>(<span style="color:#008855">String</span> <span style="color:#000000">name</span>, <span style="color:#008855">int</span> <span style="color:#000000">age</span>, <span style="color:#008855">double</span> <span style="color:#000000">height</span>) {
            <span style="color:#770088">this</span>.<span style="color:#000000">name</span> <span style="color:#981a1a">=</span> <span style="color:#000000">name</span>;
            <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">=</span> <span style="color:#000000">age</span>;
            <span style="color:#770088">this</span>.<span style="color:#000000">height</span> <span style="color:#981a1a">=</span> <span style="color:#000000">height</span>;
        }
    ​
        <span style="color:#aa5500">/**</span>
         <span style="color:#aa5500">* 获取</span>
         <span style="color:#aa5500">*</span>
         <span style="color:#aa5500">* @return name</span>
         <span style="color:#aa5500">*/</span>
        <span style="color:#770088">public</span> <span style="color:#008855">String</span> <span style="color:#000000">getName</span>() {
            <span style="color:#770088">return</span> <span style="color:#000000">name</span>;
        }
    ​
        <span style="color:#aa5500">/**</span>
         <span style="color:#aa5500">* 设置</span>
         <span style="color:#aa5500">*</span>
         <span style="color:#aa5500">* @param name</span>
         <span style="color:#aa5500">*/</span>
        <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">setName</span>(<span style="color:#008855">String</span> <span style="color:#000000">name</span>) {
            <span style="color:#770088">this</span>.<span style="color:#000000">name</span> <span style="color:#981a1a">=</span> <span style="color:#000000">name</span>;
        }
    ​
        <span style="color:#aa5500">/**</span>
         <span style="color:#aa5500">* 获取</span>
         <span style="color:#aa5500">*</span>
         <span style="color:#aa5500">* @return age</span>
         <span style="color:#aa5500">*/</span>
        <span style="color:#770088">public</span> <span style="color:#008855">int</span> <span style="color:#000000">getAge</span>() {
            <span style="color:#770088">return</span> <span style="color:#000000">age</span>;
        }
    ​
        <span style="color:#aa5500">/**</span>
         <span style="color:#aa5500">* 设置</span>
         <span style="color:#aa5500">*</span>
         <span style="color:#aa5500">* @param age</span>
         <span style="color:#aa5500">*/</span>
        <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">setAge</span>(<span style="color:#008855">int</span> <span style="color:#000000">age</span>) {
            <span style="color:#770088">this</span>.<span style="color:#000000">age</span> <span style="color:#981a1a">=</span> <span style="color:#000000">age</span>;
        }
    ​
        <span style="color:#aa5500">/**</span>
         <span style="color:#aa5500">* 获取</span>
         <span style="color:#aa5500">*</span>
         <span style="color:#aa5500">* @return height</span>
         <span style="color:#aa5500">*/</span>
        <span style="color:#770088">public</span> <span style="color:#008855">double</span> <span style="color:#000000">getHeight</span>() {
            <span style="color:#770088">return</span> <span style="color:#000000">height</span>;
        }
    ​
        <span style="color:#aa5500">/**</span>
         <span style="color:#aa5500">* 设置</span>
         <span style="color:#aa5500">*</span>
         <span style="color:#aa5500">* @param height</span>
         <span style="color:#aa5500">*/</span>
        <span style="color:#770088">public</span> <span style="color:#008855">void</span> <span style="color:#000000">setHeight</span>(<span style="color:#008855">double</span> <span style="color:#000000">height</span>) {
            <span style="color:#770088">this</span>.<span style="color:#000000">height</span> <span style="color:#981a1a">=</span> <span style="color:#000000">height</span>;
        }
    ​
        <span style="color:#555555">@Override</span>
        <span style="color:#770088">public</span> <span style="color:#008855">String</span> <span style="color:#000000">toString</span>() {
            <span style="color:#770088">return</span> <span style="color:#aa1111">"Student{"</span> <span style="color:#981a1a">+</span>
                    <span style="color:#aa1111">"name='"</span> <span style="color:#981a1a">+</span> <span style="color:#000000">name</span> <span style="color:#981a1a">+</span> <span style="color:#aa1111">'\''</span> <span style="color:#981a1a">+</span>
                    <span style="color:#aa1111">", age="</span> <span style="color:#981a1a">+</span> <span style="color:#000000">age</span> <span style="color:#981a1a">+</span>
                    <span style="color:#aa1111">", height="</span> <span style="color:#981a1a">+</span> <span style="color:#000000">height</span> <span style="color:#981a1a">+</span>
                    <span style="color:#aa1111">'}'</span>;
        }
    }
    ​</span>

  • SetDemo07中创建TreeSet对象的时候,使用比较器对象指定排序规则

    <span style="background-color:#f8f8f8"><span style="color:#770088">package</span> <span style="color:#0000ff">com</span>.<span style="color:#000000">itheima</span>.<span style="color:#000000">ts02</span>;
    ​
    ​
    <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">Comparator</span>;
    <span style="color:#770088">import</span> <span style="color:#000000">java</span>.<span style="color:#000000">util</span>.<span style="color:#000000">TreeSet</span>;
    ​
    <span style="color:#aa5500">/**</span>
     <span style="color:#aa5500">* TreeSet存储自定义类型对象:Student。使用比较器排序</span>
     <span style="color:#aa5500">*/</span>
    <span style="color:#770088">public</span> <span style="color:#770088">class</span> <span style="color:#0000ff">SetDemo07</span> {
        <span style="color:#770088">public</span> <span style="color:#770088">static</span> <span style="color:#008855">void</span> <span style="color:#000000">main</span>(<span style="color:#008855">String</span>[] <span style="color:#000000">args</span>) {
            <span style="color:#aa5500">// 创建集合对象,泛型为Student。并指定比较器对象,在比较器对象中实现排序规则</span>
            <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><</span><span style="color:#000000">Student</span><span style="color:#981a1a">></span> <span style="color:#000000">stus</span> <span style="color:#981a1a">=</span> <span style="color:#770088">new</span> <span style="color:#000000">TreeSet</span><span style="color:#981a1a"><></span>((<span style="color:#000000">stu1</span>, <span style="color:#000000">stu2</span>) <span style="color:#981a1a">-></span> {
    ​
                <span style="color:#aa5500">// 让该方法的返回值和成员变量相关 name age height</span>
                <span style="color:#008855">int</span> <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#116644">0</span>;
                <span style="color:#aa5500">// 首先按照年龄比</span>
                <span style="color:#000000">result</span> <span style="color:#981a1a">=</span> <span style="color:#000000">stu1</span>.<span style="color:#000000">getAge</span>() <span style="color:#981a1a">-</span> <span style="color:#000000">stu2</span>.<span style="color:#000000">getAge</span>();
    ​
                <span style="color:#aa5500">// 年龄相同则必身高</span>
                <span style="color:#000000">result</span>  <span style="color:#981a1a">=</span>  <span style="color:#000000">result</span>  <span style="color:#981a1a">==</span>  <span style="color:#116644">0</span> <span style="color:#981a1a">?-</span> (<span style="color:#008855">int</span>) (<span style="color:#000000">stu1</span>.<span style="color:#000000">getHeight</span>() <span style="color:#981a1a">-</span> <span style="color:#000000">stu2</span>.<span style="color:#000000">getHeight</span>()) : <span style="color:#000000">result</span>;
    ​
                <span style="color:#aa5500">// 身高也相同,比名称</span>
                <span style="color:#770088">return</span> <span style="color:#000000">result</span> <span style="color:#981a1a">==</span> <span style="color:#116644">0</span> <span style="color:#981a1a">?</span> <span style="color:#000000">stu1</span>.<span style="color:#000000">getName</span>().<span style="color:#000000">compareTo</span>(<span style="color:#000000">stu2</span>.<span style="color:#000000">getName</span>()) : <span style="color:#000000">result</span>;
    ​
            });
            <span style="color:#aa5500">// 创建元素对象并添加</span>
            <span style="color:#aa5500">// TreeSet不能直接存储自定义对象,因为TreeSet集合不知道对元素进行怎样的排序</span>
            <span style="color:#aa5500">// ClassCastException: com.itheima.ts.Student cannot be cast to java.lang.Comparable</span>
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"华强"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">187.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"芳芳"</span>, <span style="color:#116644">88</span>, <span style="color:#116644">158.0</span>));
            <span style="color:#000000">stus</span>.<span style="color:#000000">add</span>(<span style="color:#770088">new</span> <span style="color:#000000">Student</span>(<span style="color:#aa1111">"Ikun"</span>, <span style="color:#116644">18</span>, <span style="color:#116644">188.0</span>));
    ​
            <span style="color:#aa5500">// 遍历集合中元素,验证set特点</span>
            <span style="color:#000000">System</span>.<span style="color:#000000">out</span>.<span style="color:#000000">println</span>(<span style="color:#aa1111">"stus = "</span> <span style="color:#981a1a">+</span> <span style="color:#000000">stus</span>);
    ​
    ​
        }
    }</span>

1.5.5 两种排序方式对比

不同:实现排序的方式

  • 自然排序:元素实体类实现Comparable接口,重写compareTo方法,根据返回值进行排序。

  • 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序。

  • 应用场景:在使用的时候,默认使用自然排序;当自然排序不满足现在的需求时,使用比较器排序

相同:排序的底层逻辑

compareTo/compare方法返回值与排序结果的关系

  1. 如果返回值为负数,表示当前存入的元素是较小值,存左边

  2. 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存

  3. 如果返回值为正数,表示当前存入的元素是较大值,存右边

  • 不需要记忆,随便写谁在前在后无所谓,运行之后如果顺序对了,就不用改了;顺序不对,前面加个-号,或者改下顺序即可。

1.6 单列集合汇总

  • 单列集合顶层父接口:Collection

    • 内部规定了单列集合通用的功能:添加元素、获取长度、(删改查)

  • 分为两个分支

    • List:通用特点:有序(存取顺序一致)、可重复,有索引

      • ArrayList:底层是数组,查询快,增删慢

      • LinkedList:底层是双向链表,查询慢、增删快

    • Set:通用特点:无序(存取顺序不一致)、唯一、无索引

      • HashSet:底层是Hash表,增删改查性能均衡

        • JDK8以前是数组+链表

        • JDK8及以后是数组+链表+红黑树

      • LinkedHashSet:底层是Hash表+双向链表记录顺序,所以有序,同样唯一、无索引

      • TreeSet:底层是红黑树,可以排序,唯一,无索引

      • 图示:

      • ==2. 可变参数==

        2.1 概述

      • 可变参数:在方法定义时可以指定个数不确定的参数,对于同一方法可以使用不同个数的实参调用。

        eg:定义个print(某个类型 可变参数)方法,可以通过以下方式调用

        print("hello");
        print("hello","lisi");
        print("hello","zhangsan", "vsunks")
            ......

      • 格式:

        参数一般在方法定义和调用时使用,可变参数一般用作形参(方法定义位置)

      • 格式:数据类型... 变量名

      • 范例:int... num

      • 放在方法签名上:修饰符 返回值类型 方法名(数据类型... 变量名){}

      • 范例:public static int sum(int... num){}

      • 代码演示:

      • KbArgsDemo01.java

        package com.itheima;
        ​
        /**
         * 可变参数入门案例
         */
        public class KbArgsDemo01 {
            public static void main(String[] args) {
                // 2. 调用sum方法,使用可变参数
                // 2.1 调用方式1:准备一个元素类型和可变参数类型一致的数组
                int[] ints = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        ​
                int sum = sum(ints);
                System.out.println("sum = " + sum);
        ​
                // 2.2 调用方式2:传递多个散列的值/变量,值/变量的类型与可变参数一致即可;
                // 会自动将这几个散列的实参封装到可变参数中
                System.out.println("sum(1,2,3,4,5) = " + sum(1, 2, 3, 4, 5));
        ​
        ​
            }
        ​
            // 1. 定义方法,并在方法形参位置使用可变参数
            // 格式:数据类型... 变量名
            public static int sum(int... nums) {
                int sum = 0;
                // 1.1 在方法内部,可变参数可以当做数组来使用
                for (int num : nums) {
                    sum += num;
                }
                return sum;
            }
        }

      • 2.2 注意事项:

      • 可变参数本质就是一个数组。

      • 可变参数只能作为最后一个形参

        • 可变参数相当于一个大胖子,吃实参,有一个吃一个;如果放在前面,调用方法时,传递的所有实参都会被可变参数吃掉。

      • 演示代码

        package com.itheima;
        ​
        /**
         * 可变参数注意事项
         *
         * @Author Vsunks.v
         * @Date 2023/3/2 11:48
         * @Blog blog.sunxiaowei.net/996.mba
         * @Description: 可变参数注意事项
         */
        public class KbArgsDemo02 {
            public static void main(String[] args) {
                // 2. 调用sum方法,使用可变参数
                // 2.1 调用方式1:准备一个元素类型和可变参数类型一致的数组
                int[] ints = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        ​
                int sum = sum(0, ints);
                System.out.println("sum = " + sum);
        ​
                // 2.2 调用方式2:传递多个散列的值/变量,值/变量的类型与可变参数一致即可;
                // 会自动将这几个散列的实参封装到可变参数中
                System.out.println("sum(1,2,3,4,5) = " + sum(1, 2, 3, 4, 5));
        ​
        ​
            }
        ​
            // 1. 定义方法,可变参数放在最后
            public static int sum(int num0, int... nums) {
                int sum = num0;
                // 1.1 在方法内部,可变参数可以当做数组来使用
                for (int num : nums) {
                    sum += num;
                }
                return sum;
            }
        ​
            // 1. 定义方法,可变参数放在前面
            // Vararg parameter must be the last in the list 可变参数必须是列表的最后一个
           /*  public static int sum(int... nums, int num0) {
                int sum = num0;
                // 1.1 在方法内部,可变参数可以当做数组来使用
                for (int num : nums) {
                    sum += num;
                }
                return sum;
            } */
        }
        ​
      • 2.3 应用场景

        可变参数的本质就是数组(容器)。

        可变参数之于数组,在更少的场景可以更简单的使用数组这个容器。

      • 更少的场景:方法形参中最后一个位置的数组可以使用可变参数替换;在方法中可以把他当做数组使用。

      • 更简单使用:调用方法时,不需要创建数组对象并传递,而是直接传递多个散列的实参。

      • 形参上有数组,调用的时候想方便(不想把多个数据封装成一个数组再传递),就可以使用可变参数。

        应用场景举例:

      • JDK9中添加了众多快捷创建集合的方法。

      • Collections工具类中,有些方法也用到了可变参数。

      • 3. Collections

        3.1 概述

        Collections是Collection单列集合的工具类,内部封装了一些操作单列集合的方法。

        该工具类中用到了可变参数。

        经验分享:

      • 在Java中,xxxs多是xxx的工具类。eg:Arrays是操作数组的工具类,Collections是操作Collection的工具类

      • 3.2 成员方法

        Collections中常见成员方法如下:

        方法签名方法说明
        public static <T> boolean addAll(Collection c, T... e)向集合批量添加元素
        public static void shuffle(List<?> list)打乱List集合中元素顺序
        public static <T> void sort(List<T> list)对List集合中元素排序(自然排序)
        public static <T> void sort(List<T> list, Comparator c)对List集合中元素排序(比较器排序)

        注释:

        // 1.public static <T> boolean addAll(Collection<? super T> c, T...e) 向集合批量添加元素
        // 2.public static void shuffle(List<?> list):打乱List集合中元素顺序
        // 3.public static <T> void sort(List<T> list): 对List集合中元素排序(自然排序)
        // 4.public static <T> void sort(List<T> list, Comparator c) 对List集合中元素排序(比较器排序)

        演示代码:

      • CollectionsDemo.java

        import com.itheima.Student;
        ​
        import java.util.ArrayList;
        import java.util.Collections;
        import java.util.Comparator;
        ​
        /**
         * Collections工具类演示
         */
        public class CollectionsDemo {
            public static void main(String[] args) {
        ​
                // 1.public static <T> boolean addAll(Collection<? super T> c, T...e) 向集合批量添加元素
                ArrayList<String> names = new ArrayList<>();
                Collections.addAll(names,"Ikun","芳芳","芳芳的Ikun","Ikun真爱芳芳");
                System.out.println("names = " + names);
        ​
                // 2.public static void shuffle(List<?> list):随机打乱List集合中元素顺序
                Collections.shuffle(names);
                System.out.println("names = " + names);
        ​
                ArrayList<Student> stus = new ArrayList<>();
                stus.add(new Student("Ikun",18,188.0));
                stus.add(new Student("华强",19,189.0));
                stus.add(new Student("芳芳",88,158.0));
                stus.add(new Student("Ikun",18,188.0));
        ​
        ​
                System.out.println("stus = " + stus);
                // 3.public static <T> void sort(List<T> list): 对List集合中元素排序(自然排序)
                Collections.sort(stus);
                System.out.println("stus = " + stus);
        ​
        ​
                System.out.println("stus = " + stus);
                // 元素本身具有比较能力时,如果指定了比较器,优先使用比较器的规则。
                // 4.public static <T> void sort(List<T> list, Comparator c) 对List集合中元素排序(比较器排序)
                Collections.sort(stus, (stu1, stu2) -> {
                    // 让该方法的返回值和成员变量相关 name age height
                    int result = 0;
                    // 首先按照年龄比
                    result = -stu1.getAge() + stu2.getAge();
        ​
                    // 年龄相同则必身高
                    result = result == 0 ?- (int) (stu1.getHeight() - stu2.getHeight()) : result;
        ​
                    // 身高也相同,比名称
                    return result == 0 ? stu1.getName().compareTo(stu2.getName()) : result;
                });
                System.out.println("stus = " + stus);
        ​
        ​
            }
        }
      • 3.3 案例:斗地主

        需求:

        一副牌:54张

        三个人斗地主。

      • 元素实体类Student.java,实现可比较接口,重写compareTo方法
        package com.itheima;
        ​
        /**
         * Collections排序:自然排序
         */
        public class Student implements Comparable<Student> {
            private String name;
            private int age;
            private double height;
        ​
        ​
            public Student() {
            }
        ​
            public Student(String name, int age, double height) {
                this.name = name;
                this.age = age;
                this.height = height;
            }
        ​
            /**
             * 获取
             *
             * @return name
             */
            public String getName() {
                return name;
            }
        ​
            /**
             * 设置
             *
             * @param name
             */
            public void setName(String name) {
                this.name = name;
            }
        ​
            /**
             * 获取
             *
             * @return age
             */
            public int getAge() {
                return age;
            }
        ​
            /**
             * 设置
             *
             * @param age
             */
            public void setAge(int age) {
                this.age = age;
            }
        ​
            /**
             * 获取
             *
             * @return height
             */
            public double getHeight() {
                return height;
            }
        ​
            /**
             * 设置
             *
             * @param height
             */
            public void setHeight(double height) {
                this.height = height;
            }
        ​
            @Override
            public String toString() {
                return "Student{" +
                        "name='" + name + '\'' +
                        ", age=" + age +
                        ", height=" + height +
                        '}';
            }
        ​
            @Override
            public int compareTo(Student stu) {
                // 让该方法的返回值和成员变量相关 name age height
                int result = 0;
                // 首先按照年龄比
                result = this.age - stu.age;
        ​
                // 年龄相同则必身高
                result = result == 0 ? (int) (this.height - stu.height) : result;
        ​
                // 身高也相同,比名称
                return result == 0 ? this.name.compareTo(stu.name) : result;
            }
        }
      • -

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值