• Map集合继承结构


    ✅作者简介:大家好我是@每天都要敲代码,一位材料转码农的选手,希望一起努力,一起进步!
    📃个人主页:@每天都要敲代码的个人主页
    💬推荐一款模拟面试、刷题神器,从基础到大厂面试题👉点击跳转刷题网站进行注册学习

    目录

    🥅Map集合概述 

    🥅Map接口常用的方法

    🥅哈希表(散列表)数据结构

    🥅同时重写HashCode和equals

    🥅HashMap和Hashtable的区别

    🥅Properties类

    🥅TreeSet(TreeMap)集合

    🥅自平衡二叉树数据结构

    🥅实现比较器接口

    🥅集合工具类Collections


    🥅Map集合概述 

    (1)Map和Collection没有继承关系。

    (2)Map集合以key和value的方式存储数据:

             键值对key和value都是引用数据类型。

             key和value都是存储对象的内存地址。

             key起到主导的地位,value是key的一个附属品。

    (3)HashMap: 底层是哈希表。

    (4)HashTable: 底层也是哈希表,只不过线程是安全的,效率较低使用较少。

    (5)Prorerties:线程是安全的,并且key和value只能储存字符串String类型

    (6)TreeMap:底层是二叉树,TreeMap集合的key可以自动按照大小顺序排序。

     ⭐️Map集合继承结构图

    🥅Map接口常用的方法

    Map接口中常用方法:
             V put(K key, V value)  向Map集合中添加键值对
             V get(Object key)  通过key获取value
             void clear()    清空Map集合
             boolean containsKey(Object key)  判断Map中是否包含某个key
             boolean containsValue(Object value)  判断Map中是否包含某个value
             boolean isEmpty()   判断Map集合中元素个数是否为0
             V remove(Object key)  通过key删除键值对
             int size()  获取Map集合中键值对的个数。
             Collection values()  获取Map集合中所有的value(所有的values是一个Collection集合)
             Set keySet()  获取Map集合所有的key(所有的键(Key)是一个Set集合)
             Set> entrySet()  将Map集合转换成Set集合
             对于Map结合转换成Set集合怎么理解?假设现在有一个Map集合,如下所示:
             map1集合对象
              

    1.    key             value
    2.             ----------------------------
    3.             1               zhangsan
    4.             2               lisi
    5.             3               wangwu
    6.             4               zhaoliu

            Set set = map1.entrySet();
            set集合对象的形式,把Map集合的两个值通过=的方式撮合成一个了
          

    1.    1=zhangsan 
    2.         2=lisi  
    3.         3=wangwu
    4.         4=zhaoliu 

    注意:Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是 Map.Entry;Map.Entry和String一样,都是一种类型的名字,只不过:Map.Entry是静态内部类,是Map中的静态内部类       

     ⭐️回顾静态内部类

    1. package com.bjpowernode.javase.map;
    2. public class Myclass {
    3. //声明一个静态内部类
    4. public static class InnerClass{
    5. // 静态内部类的静态方法
    6. public static void m1(){
    7. System.out.println("静态内部类的静态方法执行!");
    8. }
    9. // 静态内部类的实例方法
    10. public void m2(){
    11. System.out.println("静态内部类的实例方法执行!");
    12. }
    13. }
    14. public static void main(String[] args) {
    15. // 静态方法执行,类名:Myclass.InnerClass
    16. Myclass.InnerClass.m1();
    17. // 创建静态内部类对象
    18. Myclass.InnerClass mi = new Myclass.InnerClass();
    19. mi.m2(); //执行
    20. //给一个Set集合;改Set集合中存储的对象是:MyClass.InnerClass类型
    21. Set set = new HashSet<>();
    22. }
    23. }

    ⭐️例1:

    1. package com.bjpowernode.javase.map;
    2. import java.util.Collection;
    3. import java.util.HashMap;
    4. import java.util.Map;
    5. public class MapTest02 {
    6. public static void main(String[] args) {
    7. // 创建Map集合对象
    8. Map map = new HashMap<>();
    9. // 1、向Map集合中添加键值对;put方法
    10. map.put(1,"张三"); // 1是自动装箱
    11. map.put(2,"李四");
    12. map.put(3,"王五");
    13. // 2、通过key获取value;get方法
    14. String str = map.get(1);
    15. System.out.println(str); //张三
    16. // 3、获取Map集合中键值对的个数;size方法
    17. System.out.println(map.size()); //3
    18. // 4、 通过key删除键值对;remove方法
    19. map.remove(1);
    20. System.out.println(map.size()); //2
    21. // contains方法底层调用的都是equals进行比对的,所以自定义的类型需要重写equals方法。
    22. // 5、判断Map中是否包含某个key;containsKey方法
    23. boolean b1 = map.containsKey(2);
    24. System.out.println(b1); // true
    25. // 6、判断Map中是否包含某个value;containsValue方法
    26. boolean b2 = map.containsValue("李四");
    27. System.out.println(b2); // true
    28. // 7、获取所有的value;values方法
    29. Collection c = map.values();
    30. // 遍历打印这个集合
    31. for(String s:c){ //增加for循环进行打印
    32. System.out.println(s);
    33. }
    34. // 8、清空集合;clear方法
    35. map.clear();
    36. System.out.println(map.size()); //0
    37. // 9、判断Map集合是否为空;isEmpty方法
    38. System.out.println(map.isEmpty()); // true
    39. }
    40. }

    ⭐️例2:遍历Map结合(重点)

    1. package com.bjpowernode.javase.map;
    2. import java.util.HashMap;
    3. import java.util.Iterator;
    4. import java.util.Map;
    5. import java.util.Set;
    6. // Map集合遍历
    7. public class MapTest03 {
    8. public static void main(String[] args) {
    9. //第一种方式:获取所有的key,通过遍历key,来遍历value
    10. Map map = new HashMap<>();
    11. map.put(1, "zhangsan");
    12. map.put(2, "lisi");
    13. map.put(3, "wangwu");
    14. // 先获取所有的key,所有的key是一个Set集合
    15. Set keys = map.keySet();
    16. // 在遍历key,通过key获取value
    17. // 方法1:利用迭代器
    18. Iterator it = keys.iterator();
    19. while (it.hasNext()){
    20. Integer key = it.next(); //获取其中一个key
    21. String value = map.get(key); //通过key获取value
    22. System.out.println(key+"="+value);
    23. }
    24. // 方法2:foreach
    25. for(Integer key:keys){
    26. System.out.println(key+"="+map.get(key));
    27. }
    28. //第二种方式:   Set> entrySet()  将Map集合转换成Set集合
    29. Set> set = map.entrySet();
    30. // 方法1:使用迭代器进行打印
    31. Iterator> it2 = set.iterator();
    32. while(it2.hasNext()){
    33. Map.Entry node = it2.next(); //取出一个节点
    34. Integer key = node.getKey(); //取出节点的key
    35. String value = node.getValue(); //取出节点的value
    36. System.out.println(key+"="+value);
    37. }
    38. // 方法2:foreach方式
    39. // 这种方式效率比较高,因为获取key和value都是直接从node对象中获取的属性值。
    40. // 这种方式比较适合于大数据量。
    41. for( Map.Entry node:set){
    42. System.out.println(node.getKey()+"="+node.getValue());
    43. }
    44. }
    45. }

    🥅哈希表(散列表)数据结构

    1、HashMap集合底层是哈希表/散列表的数据结构。
    2、哈希表是一个怎样的数据结构呢?
            哈希表是一个数组和单向链表的结合体
            数组:在查询方面效率很高,随机增删方面效率很低。
            单向链表:在随机增删方面效率较高,在查询方面效率很低。
            哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。
    3、HashMap集合底层的源代码:
       

    1.     public class HashMap{
    2.             // HashMap底层实际上就是一个数组。(一维数组)
    3.             Node[] table;
    4.             // 静态的内部类HashMap.Node
    5.             static class Node {
    6.                 final int hash; // 哈希值(哈希值是key的hashCode()方法的执行结果。hash值通过哈希函数/算法,可以转换存储成数组的下标。)
    7.                 final K key; // 存储到Map集合中的那个key
    8.                 V value; // 存储到Map集合中的那个value
    9.                 Node next; // 下一个节点的内存地址。
    10.             }
    11.         }

            哈希表/散列表:一维数组,这个数组中每一个元素是一个单向链表。(数组和链表的结合体。)
    4、最主要掌握的是:
            map.put(k,v)实现原理:
            第一步:先将k,v封装到Node对象当中
            第二步:底层会调用k的hashCode()方法得出hash值
            然后通过哈希函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素;就把Node添加到这个位置上;如果下标对应的位置上有链表,此时会拿着k和链表上每一个结点的k进行equals比较,如果所有的equals方法返回的都是false,那么将这个新结点添加到链表的结尾如果其中一个equals返回了true,那么这个结点的value将会被覆盖
            v = map.get(k)实现原理:
            先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下标;通过数组下标快速定位到某个位置上,如果这个位置上什么都没有,返回null;如果这个位置上有单链表,那么会拿着参数k和单链表上的每个结点中的k进行equals;如果所有的equals方法返回的都是false,那么get方法返回null;只要其中一个结点的k和参数k在equals的时候返回true,那么此时这个结点的value就是我们要找的value,get方法最终返回这个要找的value
            重点:通过上面的讲解可以得出HashMap集合的key,会先后调用两个方法, 一个方法是hashCode(),一个方法是equals(),这两个方法都需要重写
            注意:同一个单向链表上所有节点的hash值相同,因为他们的数组下标是一样的但同一个链表上k和k的equals方法肯定返回的是false,都不相同(无序不可重复)
        5、HashMap集合的key部分特点无序,不可重复
            为什么无序? 因为不一定挂到哪个单向链表上。
            不可重复是怎么保证的? equals方法来保证HashMap集合的key不可重复。
            如果key重复了,value会覆盖。
            放在HashMap集合key部分的元素其实就是放到HashSet集合中了。
            所以HashSet集合中的元素也需要同时重写hashCode()+equals()方法。

        6、哈希表HashMap使用不当时无法发挥性能!(我们考虑两个极端情况)
            假设将所有的hashCode()方法返回值固定为某个值,那么会导致底层哈希表变成了
            纯单向链表。这种情况我们成为:散列分布不均匀。
            什么是散列分布均匀?
                假设有100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,
                是散列分布均匀的。
            假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?
                不行,因为这样的话导致底层哈希表就成为一维数组了,没有链表的概念了。
                也是散列分布不均匀。
            所以要想散列分布均匀,需要你重写hashCode()方法时有一定的技巧。
        7、重点:放在HashMap集合key部分的元素,以及放在HashSet集合中的元素,需要同时重写hashCode和equals方法。
        8、HashMap集合的默认初始化容量是16,默认加载因子是0.75

             扩容:扩容后的容量是原容量的2倍

            这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。
            重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,
            这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。

    1. package com.bjpowernode.javase.map;
    2. import java.util.HashMap;
    3. import java.util.Map;
    4. import java.util.Set;
    5. public class HashMapTest01 {
    6. public static void main(String[] args) {
    7. // 测试HashMap 集合key部分的元素特点
    8. // Integer是key,它的hashCode和equals都重写了
    9. Map map = new HashMap<>();
    10. map.put(111,"zhangsan");
    11. map.put(222,"lisi");
    12. map.put(333,"wangwu");
    13. map.put(333,"zhaoliu"); //key重复,value会覆盖上面的值
    14. // 元素个数
    15. System.out.println(map.size()); //3
    16. // 遍历
    17. Set> set = map.entrySet();
    18. for(Map.Entry entry:set){
    19. System.out.println(entry.getKey()+"="+entry.getValue());
    20. }
    21. /*333=zhaoliu
    22. 222=lisi
    23. 111=zhangsan
    24. 验证结果:无序不可重复!
    25. */
    26. }
    27. }

    🥅同时重写HashCode和equals

    1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!equals方法有可能调用,也有可能不调用。
        拿put(k,v)举例,什么时候equals不会调用?
            k.hashCode()方法返回哈希值,
            哈希值经过哈希算法转换成数组下标。
            数组下标位置上如果是null,equals不需要执行。
        拿get(k)举例,什么时候equals不会调用?
            k.hashCode()方法返回哈希值,
            哈希值经过哈希算法转换成数组下标。
            数组下标位置上如果是null,equals不需要执行。

    2、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。
    并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
        equals方法返回true表示两个对象相同,在同一个单向链表上比较。
        那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
        所以hashCode()方法的返回值也应该相同。

    3、hashCode()方法和equals()方法可以直接使用IDEA工具生成,但是这两个方法需要同时生成。

    4、终极结论:
        放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

    5、对于哈希表数据结构来说:
        如果o1和o2的hash值相同,一定是放到同一个单向链表上。
        如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

    6、JDK8对HashMap集合的改进:如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树;当红黑树节点数量小于6时,会重新把红黑树变成单向链表;这种方式也是为了提高检索效率,二叉树的检索效率会再次缩小扫描范围,提高效率

    ⭐学生类

    1. package com.bjpowernode.javase.map;
    2. import java.util.Objects;
    3. public class Student {
    4. private String name;
    5. // 构造方法
    6. public Student() {
    7. }
    8. public Student(String name) {
    9. this.name = name;
    10. }
    11. // setter and getter
    12. public String getName() {
    13. return name;
    14. }
    15. public void setName(String name) {
    16. this.name = name;
    17. }
    18. // 同时生成equals方法和hashCode方法
    19. public boolean equals(Object o) {
    20. if (this == o) return true;
    21. if (o == null || getClass() != o.getClass()) return false;
    22. Student student = (Student) o;
    23. return Objects.equals(name, student.name);
    24. }
    25. public int hashCode() {
    26. return Objects.hash(name);
    27. }
    28. }

    ⭐测试 

    1. package com.bjpowernode.javase.map;
    2. import java.util.HashSet;
    3. import java.util.Set;
    4. public class HashMapTest02 {
    5. public static void main(String[] args) {
    6. Student s1 = new Student("张三");
    7. Student s2 = new Student("张三");
    8. // 重写equlas方法之前
    9. System.out.println(s1.equals(s2)); // false
    10. // 重写equals方法之后
    11. System.out.println(s1.equals(s2)); // true
    12. System.out.println("s1的hashCode是:"+s1.hashCode()); // 1163157884(重写hashCode之后:774920)
    13. System.out.println("s2的hashCode是:"+s2.hashCode()); // 1956725890(重写hashCode之后:774920)
    14. System.out.println(s1.hashCode() == s2.hashCode()); // false
    15. // s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,
    16. // 那么往HashSet集合中放的话,按说只能放进去1个。(HashSet集合特点:无序不可重复)
    17. Set students = new HashSet<>();
    18. students.add(s1);
    19. students.add(s2);
    20. System.out.println(students.size()); //2,但按理说应该是1,无序不可重复!重写hashCode方法
    21. System.out.println(students.size()); // 1 重写hashCode以后结果就是1
    22. }
    23. }

    ❤️HashMap的key可以为null

    HashMap集合key部分允许null吗?
    答:允许但是要注意:HashMap集合的key null值只能有一个;多了会被后面的覆盖

    1. package com.bjpowernode.javase.map;
    2. import java.util.HashMap;
    3. import java.util.Map;
    4. public class HashMapTest03 {
    5. public static void main(String[] args) {
    6. Map map = new HashMap();
    7. // HashMap集合允许key为null
    8. map.put(null,null);
    9. System.out.println(map.size()); // 1
    10. // key重复的话value会被覆盖
    11. map.put(null,100);
    12. System.out.println(map.size()); // 还是1
    13. // 通过key获取value
    14. System.out.println(map.get(null)); // 100
    15. }
    16. }

    🥅HashMap和Hashtable的区别

    (1)Hashtable的key可以为null吗?
        Hashtable的key和value都是不能为null
        HashMap集合的key和value都是可以为null

    (2)Hashtable方法都带有synchronized:线程安全的。线程安全有其它的方案,Hashtable对线程的处理导致效率较低,使用较少了。

       Hashtable和HashMap一样,底层都是哈希表数据结构。

    (3)HashMap集合的默认初始化容量是16,默认加载因子是0.75
        扩容:扩容后的容量是原容量的2倍
        Hashtable初始化容量是11,默认加载因子是:0.75
        扩容:原容量 * 2 + 1

    (4)关于容量,扩容小总结:

    Collection集合:

     1、ArrayList集合初始化容量是10;扩容到原容量的1.5倍!
     2、Vector集合初始化容量是10;扩容到原容量的2倍!
     3、HashSet集合初始化容量16,初始化容量建议是2的倍数;扩容:扩容之后是原容量2倍

    Map集合:

     1、HashMap初始化容量16,默认加载因子0.75,扩容之后的容量是量的2倍。
     2、 Hashtable集合初始化容量11,默认加载因子0.75,扩容之后的容量是原容量*2 + 1

    1. package com.bjpowernode.javase.table;
    2. import java.util.HashMap;
    3. import java.util.Hashtable;
    4. import java.util.Map;
    5. public class HashtableTest01 {
    6. public static void main(String[] args) {
    7. //1、 HashMap集合的key和value都是可以为null的
    8. Map map1 = new HashMap();
    9. map1.put(null,null); // 编译和运行都没问题
    10. //2、 Hashtable的key和value都是不能为null的。
    11. Map map2 = new Hashtable();
    12. map2.put(null,"zhangsan"); // java.lang.NullPointerException
    13. map2.put(10,null); // java.lang.NullPointerException
    14. }
    15. }

    🥅Properties类

    目前只需要掌握Properties属性类对象的相关方法即可。
    Properties是一个Map集合,继承Hashtable,Properties的key和value都是String类型。
    Properties被称为属性类对象;Properties是线程安全的。

    1. package com.bjpowernode.javase;
    2. import java.util.Properties;
    3. public class PropertiesTest01 {
    4. public static void main(String[] args) {
    5. // 创建一个Properties对象
    6. Properties pro = new Properties();
    7. // 存setProperyies
    8. pro.setProperty("username","root");
    9. pro.setProperty("password","1234");
    10. pro.setProperty("driver","com.mysql.jdbc.Driver");
    11. // 通过key取value
    12. System.out.println(pro.getProperty("username")); //root
    13. System.out.println(pro.getProperty("password")); //1234
    14. System.out.println(pro.getProperty("driver")); //com.mysql.jdbc.Driver
    15. }
    16. }

    🥅TreeSet(TreeMap)集合

    1、TreeSet集合底层实际上是一个TreeMap
    2、TreeMap集合底层是一个二叉树。
    3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分了。
    4、TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序。
    称为:可排序集合。

    5、TreeSet集合有什么用呢?我们拿一个很常见的需求来作为例子:

    数据库中有很多数据:

    1.     userid  name     birth
    2.     -------------------------------------
    3.     1       zs          1980-11-11
    4.     2       ls          1980-10-11
    5.     3       ww          1981-11-11
    6.     4       zl          1979-11-11

    编写程序从数据库当中取出数据,在页面展示用户信息的时候按照生日升序或者降序。
    这个时候可以使用TreeSet集合,因为TreeSet集合放进去,拿出来就是有顺序的。

    1. package com.bjpowernode.javase.map;
    2. import java.util.TreeSet;
    3. public class TreeSetTest01 {
    4. public static void main(String[] args) {
    5. // 演示TreeSet对String是可排序的
    6. TreeSet ts = new TreeSet<>();
    7. // 添加元素
    8. ts.add("zhangsan");
    9. ts.add("lisi");
    10. ts.add("wangwu");
    11. ts.add("wangliu");
    12. // 遍历---升序排
    13. for(String s :ts){
    14. System.out.println(s);
    15. }
    16. TreeSet ts2 = new TreeSet<>();
    17. ts2.add(300);
    18. ts2.add(600);
    19. ts2.add(100);
    20. ts2.add(10);
    21. ts2.add(200);
    22. for(Integer i :ts2){
    23. System.out.println(i);
    24. }
    25. }
    26. }
    27. /*
    28. 运行结果:
    29. lisi
    30. wangliu
    31. wangwu
    32. zhangsan
    33. 10
    34. 100
    35. 200
    36. 300
    37. 600
    38. */

    ❤️TreeSet无法对自定义类型排序

    (1)对自定义的类型来说,TreeSet可以排序吗?
        以下程序中对于Person类型来说,无法排序。因为没有指定Person对象之间的比较规则。
        谁大谁小并没有说明。

    (2)以下程序运行的时候出现了这个异常:
            java.lang.ClassCastException:
                class com.bjpowernode.javase.collection.Person
                cannot be cast to class java.lang.Comparable
    (3)出现这个异常的原因是:
            Person类没有实现java.lang.Comparable接口。

    1. package com.bjpowernode.javase.map;
    2. import java.util.TreeSet;
    3. public class TreeSetTest02 {
    4. public static void main(String[] args) {
    5. // 创建对象
    6. Person p1 = new Person(32);
    7. Person p2 = new Person(20);
    8. Person p3 = new Person(30);
    9. // 创建集合
    10. TreeSet ts = new TreeSet<>();
    11. // 添加元素
    12. ts.add(p1);
    13. ts.add(p2);
    14. ts.add(p3);
    15. // 遍历打印
    16. for(Person p:ts){
    17. System.out.println(p);
    18. }
    19. }
    20. }
    21. //自定义Person类
    22. class Person{
    23. int age;
    24. public Person(int age){
    25. this.age = age;
    26. }
    27. // 重写toString()方法
    28. public String toString(){
    29. return "Person[age="+age+"]";
    30. }
    31. }

    ⭐实现Comparable接口,编写比较的逻辑

    1. package com.bjpowernode.javase.map;
    2. import java.util.TreeSet;
    3. public class TreeSetTest03 {
    4. public static void main(String[] args) {
    5. Customer c1 = new Customer(32);
    6. Customer c2 = new Customer(20);
    7. Customer c3 = new Customer(30);
    8. Customer c4 = new Customer(25);
    9. // 创建TreeSet集合
    10. TreeSet customers = new TreeSet<>();
    11. // 添加元素
    12. customers.add(c1);
    13. customers.add(c2);
    14. customers.add(c3);
    15. customers.add(c4);
    16. // 遍历
    17. for (Customer c : customers){
    18. System.out.println(c);
    19. }
    20. }
    21. }
    22. // 放在TreeSet集合中的元素需要实现java.lang.Comparable接口。
    23. // 并且实现compareTo方法。equals可以不写。
    24. class Customer implements Comparable{
    25. int age;
    26. public Customer(int age){
    27. this.age = age;
    28. }
    29. // 需要在这个方法中编写比较的逻辑,或者说比较的规则,按照什么进行比较!
    30. // k.compareTo(t.key)
    31. // 拿着参数k和集合中的每一个k进行比较,返回值可能是>0 <0 =0
    32. // 比较规则最终还是由程序员指定的:例如按照年龄升序。或者按照年龄降序。
    33. public int compareTo(Customer c) { // c1.compareTo(c2);
    34. // this是c1
    35. // c是c2
    36. // c1和c2比较的时候,就是this和c比较。
    37. /*int age1 = this.age;
    38. int age2 = c.age;
    39. if(age1 == age2){
    40. return 0;
    41. } else if(age1 > age2) {
    42. return 1;
    43. } else {
    44. return -1;
    45. }*/
    46. //return this.age - c.age; //升序
    47. return c.age - this.age; //降序
    48. }
    49. public String toString(){
    50. return "Customer[age="+age+"]";
    51. }
    52. }

    ⭐比较规则怎么写

    我们不妨编写一个,先按照年龄升序,如果年龄一样的再按照姓名升序。
    compareTo方法的返回值很重要:
            返回0表示相同,value会覆盖。
            返回>0,会继续在右子树上找。
            返回<0,会继续在左子树上找。

    1. package com.bjpowernode.javase.map;
    2. import java.util.TreeSet;
    3. //先按照年龄升序,如果年龄一样的再按照姓名升序。
    4. public class TreeSetTest04 {
    5. public static void main(String[] args) {
    6. Vip v1 = new Vip("zhangsan",18);
    7. Vip v2 = new Vip("zhangzl",18);
    8. Vip v3 = new Vip("lisi",32);
    9. Vip v4 = new Vip("wangwu",20);
    10. Vip v5 = new Vip("zhaoliu",22);
    11. // 创建集合
    12. TreeSet ts = new TreeSet<>();
    13. // 增加元素
    14. ts.add(v1);
    15. ts.add(v2);
    16. ts.add(v3);
    17. ts.add(v4);
    18. ts.add(v5);
    19. // 遍历打印
    20. for(Vip v :ts){
    21. System.out.println(v);
    22. }
    23. }
    24. }
    25. class Vip implements Comparable{
    26. String name;
    27. int age;
    28. // 构造方法
    29. public Vip(String name, int age) {
    30. this.name = name;
    31. this.age = age;
    32. }
    33. // 重写toString方法
    34. public String toString() {
    35. return "Vip{" +
    36. "name='" + name + '\'' +
    37. ", age=" + age +
    38. '}'; //Vip{name='zhangsan', age=18}
    39. }
    40. // 重写Comparable接口中中的方法
    41. public int compareTo(Vip o) {
    42. if(this.age != o.age){
    43. return this.age - o.age;
    44. }else{
    45. // 年龄相同,按照名字排序
    46. // 名字是String类型,可以直接比,调用compareTo来比较
    47. return this.name.compareTo(o.name);
    48. }
    49. }
    50. }
    51. /*
    52. 运行结果:
    53. Vip{name='zhangsan', age=18}
    54. Vip{name='zhangzl', age=18}
    55. Vip{name='wangwu', age=20}
    56. Vip{name='zhaoliu', age=22}
    57. Vip{name='lisi', age=32}
    58. */

    🥅自平衡二叉树数据结构

    1、TreeSet/TreeMap是自平衡二叉树遵循左小右大原则存放。

    2、遍历二叉树的时候有三种方式:
            前序遍历∶根左右
            中序遍历∶左根右

            后序遍历∶左右根
    注意:前中后说的是“根”的位置,根在前面是前序、根在中间是中序、根在后面是后序

    3、TreeSet/TreeMap集合采用的是:中序遍历方式,即Iterator迭代器采用的是中序遍历方式

    4、我们就拿一组数据,写出它的二叉树结构,来体验一下中序遍历的方式,例如:100 200 50 60 80 120 140 130 135 180 666 40 55

    5、中序遍历方式取出:40 50 55 60 80 100 120 130 135 140 180 200 666,按照中序遍历的方式取出来的刚好是有序的

    🥅实现比较器接口

    TreeSet集合中元素可排序的第二种方式:使用比较器的方式:
    这种实现方式是根据原码来定义的,
    如果我们使用无参的:调用的是Comparable接口里的compareTo方法如果我们使用有参的:调用的是Comparator里的compare方法
    Comparable是java.lang包下的。Comparator是java.util包下的。)
    最终的结论:
            放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:
            第一种:放在集合中的元素实现java.lang.Comparable接口。
            第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。
            Comparable和Comparator怎么选择呢?
            当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口。
            如果比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口。
            Comparator接口的设计符合OCP原则。

    1. package com.bjpowernode.javase.map;
    2. import java.util.Comparator;
    3. import java.util.TreeSet;
    4. public class TreeSetTest05 {
    5. public static void main(String[] args) {
    6. Animals animals = new Animals(15);
    7. // 创建TreeSet集合的时候,需要使用这个比较器
    8. // TreeSet animal = new TreeSet<>(); //原来这种方式就不行,没有使用比较器
    9. // TreeSet animal = new TreeSet<>(new AnimalComparator()); //new一个比较器对象传过去
    10. // 这里也可以不写Comparator接口的实现,使用匿名内部类
    11. TreeSet animal = new TreeSet<>(new Comparator() {
    12. public int compare(Animals o1, Animals o2) {
    13. return o1.age - o2.age;
    14. }
    15. });
    16. animal.add(new Animals(100));
    17. animal.add(new Animals(80));
    18. animal.add(new Animals(81));
    19. // 循遍历
    20. for(Animals a:animal){
    21. System.out.println(a);
    22. }
    23. }
    24. }
    25. class Animals{
    26. int age;
    27. // 构造方法
    28. public Animals(int age) {
    29. this.age = age;
    30. }
    31. // 重写toString方法
    32. public String toString() {
    33. return "Animals{" +
    34. "age=" + age +
    35. '}'; // Animals{age=15}
    36. }
    37. }
    38. /*
    39. // 单独在这里编写一个比较器
    40. // 比较器实现java.util.Comparator接口。
    41. class AnimalComparator implements Comparator {
    42. public int compare(Animals o1, Animals o2) {
    43. // 指定比较规则;按照年龄排序
    44. return o1.age - o2.age;
    45. }
    46. }*/

    🥅集合工具类Collections

    java.util.Collection 集合接口
    java.util.Collections 集合工具类,方便集合的操作。

    1. package com.bjpowernode.javase;
    2. import java.util.*;
    3. public class CollectionsTest {
    4. public static void main(String[] args) {
    5. // 1、 ArrayList集合不线程安全的
    6. List list = new ArrayList<>();
    7. // 通过Collections集合里的方法变成线程安全的
    8. Collections.synchronizedList(list);
    9. //排序
    10. list.add("abf");
    11. list.add("abx");
    12. list.add("abc");
    13. list.add("abe");
    14. Collections.sort(list); //排序
    15. // 打印
    16. for(String s:list){
    17. System.out.println(s); // abc abe abf abx
    18. }
    19. // 2、如果排序自己写的类,需要自己实现compareTo接口
    20. List wuGui2s = new ArrayList<>();
    21. wuGui2s.add(new WuGui2(1000));
    22. wuGui2s.add(new WuGui2(800));
    23. // 注意:对List集合中元素排序,需要保证List集合中的元素实现了:Comparable接口。
    24. Collections.sort(wuGui2s);
    25. for(WuGui2 w : wuGui2s){
    26. System.out.println(w);
    27. }
    28. // 3、对Set集合怎么排序呢?
    29. Set set = new HashSet<>();
    30. set.add("king");
    31. set.add("kingsoft");
    32. set.add("king2");
    33. set.add("king1");
    34. //Collections.sort(); //里面只能是List集合
    35. // 所以需要把Set集合转换成List集合
    36. List mylist = new ArrayList<>(set);// 把set集合转过来
    37. Collections.sort(mylist); //这样就能排序了
    38. for(String s:mylist){
    39. System.out.println(s); // king king1 king2 kingsoft
    40. }
    41. // 这种方式也可以排序。
    42. //Collections.sort(list集合, 比较器对象);
    43. }
    44. }
    45. class WuGui2 implements Comparable{
    46. int age;
    47. public WuGui2(int age){
    48. this.age = age;
    49. }
    50. public int compareTo(WuGui2 o) {
    51. return this.age - o.age;
    52. }
    53. public String toString() {
    54. return "WuGui2{" +
    55. "age=" + age +
    56. '}';
    57. }
    58. }

     结束语

    今天的分享就到这里啦!快快通过下方链接注册加入刷题大军吧!各种大厂面试真题在等你哦!

     💬刷题神器,从基础到大厂面试题👉点击跳转刷题网站

  • 相关阅读:
    linux性能分析(五)如何学习linux性能优化
    webpack配置之source-map的使用
    [附源码]计算机毕业设计JAVA基于ssm的电子网上商城
    网络协议入门 笔记一
    如何添加cookie
    SQL复杂查询-除法实现-做题理解
    【推荐】中国光伏大会资料合集25篇
    ElasticSearch从入门到精通(二)
    实时降噪(Real-time Denoising):NVIDIA Real-time Denoisers 源码剖析
    十三、函数式编程(1)
  • 原文地址:https://blog.csdn.net/m0_61933976/article/details/125789068