十三、集合进阶——双列集合

news2024/11/15 10:32:46

集合进阶——双列集合

  • 13.1 双列集合的特点
  • 13.2 Map集合
    • 13.2.1 Map集合常用的API
    • 13.2.2 Map的遍历方式
  • 13.3 HashMap
  • 13.4 LinkedHashMap
  • 13.5 TreeMap
  • 13.6 源码解析
    • HashMap源码解读
    • TreeMap源码解读
  • 13.7 可变参数
  • 13.8 Collections
  • 13.9综合练习


13.1 双列集合的特点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 双列集合一次需要存一对数据,分别为键和值
  2. 键不能重复,值可以重复
  3. 键和值是一一对应的,每一个键只能找到自己对应的值
  4. 键+值这个整体 我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”

13.2 Map集合

13.2.1 Map集合常用的API

在这里插入图片描述

public static void main(String[] args) {
        //0.创建Map集合对象
        Map<String,String> m = new HashMap<>();

        //1.添加数据
        /*put 方法的细节:
        * 添加 / 覆盖
        *
        * 在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中,方法返回null
        * 在添加数据的时候,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回。*/
        m.put("郭靖", "黄蓉");
        m.put("尹小宝", "沐剑屏");
        m.put("尹志平", "小龙女");

        System.out.println(m); // {尹小宝=沐剑屏, 尹志平=小龙女, 郭靖=黄蓉}

        //2.删除
        String result = m.remove("郭靖");
        System.out.println(result); // 黄蓉
        System.out.println(m); // {尹小宝=沐剑屏, 尹志平=小龙女}

        //3.清空
//        m.clear();
        System.out.println(m);// {}

        //4.判断是否包含
        boolean res = m.containsKey("尹小宝");
        System.out.println(res); // true

        boolean res2 = m.containsValue("沐剑屏");
        System.out.println(res2); // true

        //5.判断是否为空
        boolean emptyRes = m.isEmpty();
        System.out.println(emptyRes); // false

        //6.长度
        int size = m.size();
        System.out.println(size); // 2
    }

13.2.2 Map的遍历方式

在这里插入图片描述
在这里插入图片描述

public static void main(String[] args) {
        //Map集合的第一种遍历方式

        //0.创建Map集合对象
        Map<String, String> m = new HashMap<>();

        //1.添加元素
        m.put("尹志平", "小龙女");
        m.put("郭靖", "黄蓉");
        m.put("欧阳克", "穆念慈");

        //2.通过键找值
        //获取所有的键,把这些键放到一个单列集合中
        Set<String> keys = m.keySet();
        //遍历单列集合,得到每一个键
        //增强for
        for (String key : keys) {
//            System.out.println(key);
            //利用map集合中的键获取对应的值 get
            System.out.println(key + " = " + m.get(key));
        }
        System.out.println("=========================");

        //迭代器
        Iterator<String> it = keys.iterator();
        while (it.hasNext()){
            String key = it.next();
            System.out.println(key + " = " + m.get(key));
        }
        System.out.println("========================");

        //Lambda
        /*keys.forEach(new Consumer<String>() {
            @Override
            public void accept(String key) {
                System.out.println(key + " = " + m.get(key));
            }
        });*/

        keys.forEach(key ->System.out.println(key + " = " + m.get(key)));

    }

在这里插入图片描述
在这里插入图片描述

 public static void main(String[] args) {
        //Map集合的第二种遍历方式

        //0.创建集合对象
        Map<String,String> map = new HashMap<>();

        //1.添加数据
        map.put("标枪选手", "马超");
        map.put("人物挂件", "明世隐");
        map.put("御龙骑士", "尹志平");

        //2.通过键值对对象进行遍历
        //通过一个方法获取所有的键值对对象,返回一个Set集合
        Set<Map.Entry<String, String>> entries = map.entrySet();

        //遍历entries这个集合,去得到里面的每一个键值对对象
        //增强for
        for (Map.Entry<String, String> entry : entries) {
            System.out.println(entry.getKey() + " ==== "+ entry.getValue());
        }
        System.out.println("========================");

        //迭代器
        Iterator<Map.Entry<String, String>> it = entries.iterator();
        while (it.hasNext()){
            Map.Entry<String, String> next = it.next();
            System.out.println(next.getKey() + " ==== "+ next.getValue());
        }
        System.out.println("========================");

        //Lambda
        /*entries.forEach(new Consumer<Map.Entry<String, String>>() {
            @Override
            public void accept(Map.Entry<String, String> entry) {
                System.out.println(entry.getKey() + " ==== "+ entry.getValue());
            }
        });*/

        entries.forEach(entry-> System.out.println(entry.getKey() + " ==== "+ entry.getValue()));
    }

在这里插入图片描述

public static void main(String[] args) {
        //Map集合的第三种遍历方式

        //0.创建集合对象
        Map<String, String> map = new HashMap<>();

        //1.添加数据
        map.put("鲁迅", "这句话是我说的");
        map.put("曹操", "不可能绝对不可能");
        map.put("刘备", "接着奏乐接着舞");
        map.put("柯镇恶", "看我眼色行事");

        //2.利用Lambda表达式进行遍历
        map.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key + " === " + value);
            }
        });

        //优化
        map.forEach((key,value) -> System.out.println(key + " === " + value));
    }

13.3 HashMap

在这里插入图片描述
HashMap的特点

  1. HashMap是Map里面的一个实现类
  2. 没有额外需要学习的特有方法,直接使用Map里面的方法就可以了。
  3. 特点都是由键决定的: 无序、不重复、无索引
  4. HashMap跟HashSet底层原理是一模一样的,都是哈希表结构。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
案例:存储学生对象并遍历
需求

  • 创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。
  • 存储三个键值对元素,并遍历
  • 要求:同姓名,同年龄认为是同一个学生
package Map;

import java.util.Objects;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    /**
     * 获取
     * @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;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

public static void main(String[] args) {
        /*案例:存储学生对象并遍历
            需求
            * 创建一个HashMap集合,键是学生对象(Student),值是籍贯(String)。
            * 存储三个键值对元素,并遍历
            * 要求:同姓名,同年龄认为是同一个学生*/

        //0.创建HashMap集合
        HashMap<Student, String> hm = new HashMap<>();

        //1.创建学生对象
        Student s1 = new Student("zhangsan", 23);
        Student s2 = new Student("lisi", 24);
        Student s3 = new Student("zhangsan", 23);

        //2.添加数据
        hm.put(s1, "云南");
        hm.put(s2, "重庆");
        hm.put(s3, "广东");

        //3.遍历
        //lambda
        /*hm.forEach(new BiConsumer<Student, String>() {
            @Override
            public void accept(Student student, String s) {
                System.out.println(student.getAge() + "岁的" + student.getName() + "来自" + s + "省");
            }
        });*/

        hm.forEach((student, s) -> System.out.println(student.getAge() + "岁的" + student.getName() + "来自" + s + "省"));

        //加强for
        Set<Student> keys = hm.keySet();
        for (Student key : keys) {
            String s = hm.get(key);
            System.out.println(key.getAge() + "岁的" + key.getName() + "来自" + s + "省");
        }

        //迭代器
        Iterator<Student> it = keys.iterator();
        while (it.hasNext()) {
            Student stu = it.next();
            String s = hm.get(stu);
            System.out.println(stu.getAge() + "岁的" + stu.getName() + "来自" + s + "省");
        }

        //lambda
        keys.forEach(new Consumer<Student>() {
            @Override
            public void accept(Student student) {
                String s = hm.get(student);
                System.out.println(student.getAge() + "岁的" + student.getName() + "来自" + s + "省");
            }
        });

        keys.forEach(student -> {
            String s = hm.get(student);
            System.out.println(student.getAge() + "岁的" + student.getName() + "来自" + s + "省");
        });

		System.out.println("====================");
        //
        Set<Map.Entry<Student, String>> entries = hm.entrySet();
        for (Map.Entry<Student, String> entry : entries) {
            Student key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key +" = " +value);

        }
    }

案例:Map集合案例-统计投票人数
需求

  • 某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计处最终哪个境地昂想去的人数最多。

在这里插入图片描述

public static void main(String[] args) {
        /*案例:Map集合案例-统计投票人数
            需求
            * 某个班级80名学生,现在需要组成秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计处最终哪个境地昂想去的人数最多。*/

        //0.让学生们投票
        //定义一个数组存储四个景点
        String[] arr = {"A", "B", "C", "D"};
        //利用随机数模拟80个同学的投票
        ArrayList<String> list = new ArrayList<>();
        Random r = new Random();
        for (int i = 0; i < 80; i++) {
            int index = r.nextInt(arr.length);
            list.add(arr[index]);
        }

        //统计
        HashMap<String, Integer> hm = new HashMap<>();

        for (String name : list) {
            //判断景点是否在map集合中
            if (hm.containsKey(name)) {
                int count = hm.get(name);
                count++;
                hm.put(name, count);
            } else {
                hm.put(name, 1);
            }
        }

        //打印
        System.out.println(hm);

        //求最大值
        int max = 0;
        Set<Map.Entry<String, Integer>> entries = hm.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            int value = entry.getValue();
            if (max < value){
                max = value;
            }
        }
        System.out.println(max);

        //判断哪个景点的次数跟最大值一样
        for (Map.Entry<String, Integer> entry : entries) {
            Integer value = entry.getValue();
            if (max == value){
                System.out.println(entry.getKey());
            }
        }

    }

13.4 LinkedHashMap

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public static void main(String[] args) {
        //0.创建集合
        LinkedHashMap<String, Integer> lhm = new LinkedHashMap<>();

        //1.添加数据
        lhm.put("b", 123);
        lhm.put("a", 789);
        lhm.put("c", 456);

        //2.打印
        System.out.println(lhm); // {b=123, a=789, c=456}
    }

13.5 TreeMap

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public static void main(String[] args) {
        /*需求1:
            * 键:整数表示id
            * 值:字符串表示商品名称
            * 要求:按照id的升序排列,按照id的降序排列
            *
          */

        //0.创建TreeMap集合
        /*TreeMap<Integer,String> tm1 = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });*/

        TreeMap<Integer,String> tm1 = new TreeMap<>((o1, o2)-> o2-o1);

        //1.添加数据
        tm1.put(159,"小米手机");
        tm1.put(22,"华为手机");
        tm1.put(123,"苹果");

        //2.要求:按照id的升序排列(Integer默认排列方式)
        //按照id的降序排列
        System.out.println(tm1);
    }

package Map;

import java.util.Objects;

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

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

    /**
     * 获取
     *
     * @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;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    //方法2
    @Override
    public int compareTo(Student o) {
        //按照学生年龄的升序排列
        int i = this.getAge() - o.getAge();
        //年龄一样按照姓名的字母排列
        i = i == 0 ? this.getName().length() - o.getName().length() : i;
        return i;
    }
}

package Map;

import java.util.Objects;

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

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

    /**
     * 获取
     *
     * @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;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    //方法2
    @Override
    public int compareTo(Student o) {
        //按照学生年龄的升序排列
        int i = this.getAge() - o.getAge();
        //年龄一样按照姓名的字母排列
        i = i == 0 ? this.getName().length() - o.getName().length() : i;
        return i;
    }
}


在这里插入图片描述

public static void main(String[] args) {
        /*需求:
        * 字符串:"aababcabcdabcde"
        * 请统计字符串中每一个字符出现的次数,并按照以下格式输出
        * 输出结果:
        * a(5) b(4) c(3) d(2) e(1)
        *
        * 新的统计思想:利用map集合进行统计
        * 如果题目中没有要求对结果进行排序,默认使用HashMap
        * 如果题目中要求对结果进行排序,请使用TreeMap*/

        //0.创建Map集合
        TreeMap<Character,Integer> tm = new TreeMap<>();

        //1.定义字符串
        String s = "aababcabcdabcde";
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            //判断集合中是否存在
            if (tm.containsKey(c)){
                int count = tm.get(c);
                count++;
                tm.put(c, count);
            }else {
                tm.put(c,1);
            }
        }

        System.out.println(tm);

        //按照指定格式 a(5) b(4) c(3) d(2) e(1)
        StringBuilder sb = new StringBuilder();
       /* tm.forEach(new BiConsumer<Character, Integer>() {
            @Override
            public void accept(Character key, Integer value) {
                sb.append(key).append("(").append(value).append(") ");
            }
        });*/
        tm.forEach((key, value) -> sb.append(key).append("(").append(value).append(") "));

        //方法2
        StringJoiner sj = new StringJoiner("", "", "");
        tm.forEach((key, value) -> sj.add(key+"").add(" (").add(value+"").add(") "));
        System.out.println(sb);
    }

13.6 源码解析

在这里插入图片描述

HashMap源码解读

1.看源码之前需要了解的一些内容

Node<K,V>[] table   哈希表结构中数组的名字

DEFAULT_INITIAL_CAPACITY:   数组默认长度16

DEFAULT_LOAD_FACTOR:        默认加载因子0.75



HashMap里面每一个对象包含以下内容:
1.1 链表中的键值对对象
    包含:  
			int hash;         //键的哈希值
            final K key;      //键
            V value;          //值
            Node<K,V> next;   //下一个节点的地址值
			
			
1.2 红黑树中的键值对对象
	包含:
			int hash;         		//键的哈希值
            final K key;      		//键
            V value;         	 	//值
            TreeNode<K,V> parent;  	//父节点的地址值
			TreeNode<K,V> left;		//左子节点的地址值
			TreeNode<K,V> right;	//右子节点的地址值
			boolean red;			//节点的颜色
					


2.添加元素
HashMap<String,Integer> hm = new HashMap<>();
hm.put("aaa" , 111);
hm.put("bbb" , 222);
hm.put("ccc" , 333);
hm.put("ddd" , 444);
hm.put("eee" , 555);

添加元素的时候至少考虑三种情况:
2.1数组位置为null
2.2数组位置不为null,键不重复,挂在下面形成链表或者红黑树
2.3数组位置不为null,键重复,元素覆盖



//参数一:键
//参数二:值

//返回值:被覆盖元素的值,如果没有覆盖,返回null
public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}


//利用键计算出对应的哈希值,再把哈希值进行一些额外的处理
//简单理解:返回值就是返回键的哈希值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

//参数一:键的哈希值
//参数二:键
//参数三:值
//参数四:如果键重复了是否保留
//		   true,表示老元素的值保留,不会覆盖
//		   false,表示老元素的值不保留,会进行覆盖
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
	    //定义一个局部变量,用来记录哈希表中数组的地址值。
        Node<K,V>[] tab;
		
		//临时的第三方变量,用来记录键值对对象的地址值
        Node<K,V> p;
        
		//表示当前数组的长度
		int n;
		
		//表示索引
        int i;
		
		//把哈希表中数组的地址值,赋值给局部变量tab
		tab = table;

        if (tab == null || (n = tab.length) == 0){
			//1.如果当前是第一次添加数据,底层会创建一个默认长度为16,加载因子为0.75的数组
			//2.如果不是第一次添加数据,会看数组中的元素是否达到了扩容的条件
			//如果没有达到扩容条件,底层不会做任何操作
			//如果达到了扩容条件,底层会把数组扩容为原先的两倍,并把数据全部转移到新的哈希表中
			tab = resize();
			//表示把当前数组的长度赋值给n
            n = tab.length;
        }

		//拿着数组的长度跟键的哈希值进行计算,计算出当前键值对对象,在数组中应存入的位置
		i = (n - 1) & hash;//index
		//获取数组中对应元素的数据
		p = tab[i];
		
		
        if (p == null){
			//底层会创建一个键值对对象,直接放到数组当中
            tab[i] = newNode(hash, key, value, null);
        }else {
            Node<K,V> e;
            K k;
			
			//等号的左边:数组中键值对的哈希值
			//等号的右边:当前要添加键值对的哈希值
			//如果键不一样,此时返回false
			//如果键一样,返回true
			boolean b1 = p.hash == hash;
			
            if (b1 && ((k = p.key) == key || (key != null && key.equals(k)))){
                e = p;
            } else if (p instanceof TreeNode){
				//判断数组中获取出来的键值对是不是红黑树中的节点
				//如果是,则调用方法putTreeVal,把当前的节点按照红黑树的规则添加到树当中。
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            } else {
				//如果从数组中获取出来的键值对不是红黑树中的节点
				//表示此时下面挂的是链表
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
						//此时就会创建一个新的节点,挂在下面形成链表
                        p.next = newNode(hash, key, value, null);
						//判断当前链表长度是否超过8,如果超过8,就会调用方法treeifyBin
						//treeifyBin方法的底层还会继续判断
						//判断数组的长度是否大于等于64
						//如果同时满足这两个条件,就会把这个链表转成红黑树
                        if (binCount >= TREEIFY_THRESHOLD - 1)
                            treeifyBin(tab, hash);
                        break;
                    }
					//e:			  0x0044  ddd  444
					//要添加的元素: 0x0055   ddd   555
					//如果哈希值一样,就会调用equals方法比较内部的属性值是否相同
                    if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))){
						 break;
					}

                    p = e;
                }
            }
			
			//如果e为null,表示当前不需要覆盖任何元素
			//如果e不为null,表示当前的键是一样的,值会被覆盖
			//e:0x0044  ddd  555
			//要添加的元素: 0x0055   ddd   555
            if (e != null) {
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null){
					
					//等号的右边:当前要添加的值
					//等号的左边:0x0044的值
					e.value = value;
				}
                afterNodeAccess(e);
                return oldValue;
            }
        }
		
        //threshold:记录的就是数组的长度 * 0.75,哈希表的扩容时机  16 * 0.75 = 12
        if (++size > threshold){
			 resize();
		}
        
		//表示当前没有覆盖任何元素,返回null
        return null;
    }	

TreeMap源码解读

1.TreeMap中每一个节点的内部属性
K key;					//键
V value;				//值
Entry<K,V> left;		//左子节点
Entry<K,V> right;		//右子节点
Entry<K,V> parent;		//父节点
boolean color;			//节点的颜色




2.TreeMap类中中要知道的一些成员变量
public class TreeMap<K,V>{
   
    //比较器对象
    private final Comparator<? super K> comparator;

	//根节点
    private transient Entry<K,V> root;

	//集合的长度
    private transient int size = 0;

   

3.空参构造
	//空参构造就是没有传递比较器对象
	 public TreeMap() {
        comparator = null;
    }
	
	
	
4.带参构造
	//带参构造就是传递了比较器对象。
	public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
	
	
5.添加元素
	public V put(K key, V value) {
        return put(key, value, true);
    }

参数一:键
参数二:值
参数三:当键重复的时候,是否需要覆盖值
		true:覆盖
		false:不覆盖
		
	private V put(K key, V value, boolean replaceOld) {
		//获取根节点的地址值,赋值给局部变量t
        Entry<K,V> t = root;
		//判断根节点是否为null
		//如果为null,表示当前是第一次添加,会把当前要添加的元素,当做根节点
		//如果不为null,表示当前不是第一次添加,跳过这个判断继续执行下面的代码
        if (t == null) {
			//方法的底层,会创建一个Entry对象,把他当做根节点
            addEntryToEmptyMap(key, value);
			//表示此时没有覆盖任何的元素
            return null;
        }
		//表示两个元素的键比较之后的结果
        int cmp;
		//表示当前要添加节点的父节点
        Entry<K,V> parent;
		
		//表示当前的比较规则
		//如果我们是采取默认的自然排序,那么此时comparator记录的是null,cpr记录的也是null
		//如果我们是采取比较去排序方式,那么此时comparator记录的是就是比较器
        Comparator<? super K> cpr = comparator;
		//表示判断当前是否有比较器对象
		//如果传递了比较器对象,就执行if里面的代码,此时以比较器的规则为准
		//如果没有传递比较器对象,就执行else里面的代码,此时以自然排序的规则为准
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        } else {
			//把键进行强转,强转成Comparable类型的
			//要求:键必须要实现Comparable接口,如果没有实现这个接口
			//此时在强转的时候,就会报错。
            Comparable<? super K> k = (Comparable<? super K>) key;
            do {
				//把根节点当做当前节点的父节点
                parent = t;
				//调用compareTo方法,比较根节点和当前要添加节点的大小关系
                cmp = k.compareTo(t.key);
				
                if (cmp < 0)
					//如果比较的结果为负数
					//那么继续到根节点的左边去找
                    t = t.left;
                else if (cmp > 0)
					//如果比较的结果为正数
					//那么继续到根节点的右边去找
                    t = t.right;
                else {
					//如果比较的结果为0,会覆盖
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }
		//就会把当前节点按照指定的规则进行添加
        addEntry(key, value, parent, cmp < 0);
        return null;
    }	
	
	
	
	 private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (addToLeft)
            parent.left = e;
        else
            parent.right = e;
		//添加完毕之后,需要按照红黑树的规则进行调整
        fixAfterInsertion(e);
        size++;
        modCount++;
    }
	
	
	
	private void fixAfterInsertion(Entry<K,V> x) {
		//因为红黑树的节点默认就是红色的
        x.color = RED;

		//按照红黑规则进行调整
		
		//parentOf:获取x的父节点
		//parentOf(parentOf(x)):获取x的爷爷节点
		//leftOf:获取左子节点
        while (x != null && x != root && x.parent.color == RED) {
			
			
			//判断当前节点的父节点是爷爷节点的左子节点还是右子节点
			//目的:为了获取当前节点的叔叔节点
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
				//表示当前节点的父节点是爷爷节点的左子节点
				//那么下面就可以用rightOf获取到当前节点的叔叔节点
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
					//叔叔节点为红色的处理方案
					
					//把父节点设置为黑色
                    setColor(parentOf(x), BLACK);
					//把叔叔节点设置为黑色
                    setColor(y, BLACK);
					//把爷爷节点设置为红色
                    setColor(parentOf(parentOf(x)), RED);
					
					//把爷爷节点设置为当前节点
                    x = parentOf(parentOf(x));
                } else {
					
					//叔叔节点为黑色的处理方案
					
					
					//表示判断当前节点是否为父节点的右子节点
                    if (x == rightOf(parentOf(x))) {
						
						//表示当前节点是父节点的右子节点
                        x = parentOf(x);
						//左旋
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
				//表示当前节点的父节点是爷爷节点的右子节点
				//那么下面就可以用leftOf获取到当前节点的叔叔节点
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
		
		//把根节点设置为黑色
        root.color = BLACK;
    }
	
	
	
	
	
	
	
6.课堂思考问题:
6.1TreeMap添加元素的时候,键是否需要重写hashCode和equals方法?
此时是不需要重写的。


6.2HashMap是哈希表结构的,JDK8开始由数组,链表,红黑树组成的。
既然有红黑树,HashMap的键是否需要实现Compareable接口或者传递比较器对象呢?
不需要的。
因为在HashMap的底层,默认是利用哈希值的大小关系来创建红黑树的




6.3TreeMap和HashMap谁的效率更高?
如果是最坏情况,添加了8个元素,这8个元素形成了链表,此时TreeMap的效率要更高
但是这种情况出现的几率非常的少。
一般而言,还是HashMap的效率要更高。



6.4你觉得在Map集合中,java会提供一个如果键重复了,不会覆盖的put方法呢?
此时putIfAbsent本身不重要。
传递一个思想:
	代码中的逻辑都有两面性,如果我们只知道了其中的A面,而且代码中还发现了有变量可以控制两面性的发生。
	那么该逻辑一定会有B面。
	
	习惯:
		boolean类型的变量控制,一般只有AB两面,因为boolean只有两个值
		int类型的变量控制,一般至少有三面,因为int可以取多个值。
		



6.5三种双列集合,以后如何选择?
	HashMap LinkedHashMap TreeMap
	
	默认:HashMap(效率最高)
	如果要保证存取有序:LinkedHashMap
	如果要进行排序:TreeMap

13.7 可变参数

可变参数的练习:
假如需要定义一个方法求和,该方法可以灵活的完成如下需求:
计算2 个数据的和
计算3 个数据的和
计算4 个数据的和
计算n 个数据的和

public static void main(String[] args) {
        /*可变参数的练习:
            假如需要定义一个方法求和,该方法可以灵活的完成如下需求:
            计算2 个数据的和
            计算3 个数据的和
            计算4 个数据的和
            计算n 个数据的和*/

        //以前的方法
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int sum = getSum(arr);
        System.out.println(sum);

        /*JDK5
         * 可变参数
         * 方法形参的个数是可以发生变化的
         * 格式:属性类型...名字
         * int...args*/
        int sum2 = getSum2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println("getSum2的结果是:" + sum2);
    }

    //底层:
    //可变参数底层就是一个数组
    //只不过不需要我们自己创建, Java会帮我们创建好
    private static int getSum2(int... args) {
        int sum = 0;
        for (int arg : args) {
            sum += arg;
        }
        return sum;
    }

    private static int getSum(int[] arr) {
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        return sum;
    }

可变参数的小细节:

  1. 在方法的形参中最多只能写一个可变参数
    可变参数,理解为一个大胖子,有多少吃多少
  2. 在方法的形参当中,如果除了可变参数以外,还有其他的形参,那么可变参数要写在最后。

在这里插入图片描述

13.8 Collections

在这里插入图片描述
在这里插入图片描述

public static void main(String[] args) {
      /*
        public static <T> void sort(List<T> list)                       排序
        public static <T> void sort(List<T> list, Comparator<T> c)      根据指定的规则进行排序
        public static <T> int binarySearch (List<T> list,  T key)       以二分查找法查找元素
        public static <T> void copy(List<T> dest, List<T> src)          拷贝集合中的元素
        public static <T> int fill (List<T> list,  T obj)               使用指定的元素填充集合
        public static <T> void max/min(Collection<T> coll)              根据默认的自然排序获取最大/小值
        public static <T> void swap(List<?> list, int i, int j)         交换集合中指定位置的元素
     */


        System.out.println("-------------sort默认规则--------------------------");
        //默认规则,需要重写Comparable接口compareTo方法。Integer已经实现,按照从小打大的顺序排列
        //如果是自定义对象,需要自己指定规则
        ArrayList<Integer> list1 = new ArrayList<>();
        Collections.addAll(list1, 10, 1, 2, 4, 8, 5, 9, 6, 7, 3);
        Collections.sort(list1);
        System.out.println(list1);


        System.out.println("-------------sort自己指定规则规则--------------------------");
        Collections.sort(list1, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println(list1);

        Collections.sort(list1, (o1, o2) -> o2 - o1);
        System.out.println(list1);

        System.out.println("-------------binarySearch--------------------------");
        //需要元素有序
        ArrayList<Integer> list2 = new ArrayList<>();
        Collections.addAll(list2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println(Collections.binarySearch(list2, 9));
        System.out.println(Collections.binarySearch(list2, 1));
        System.out.println(Collections.binarySearch(list2, 20));

        System.out.println("-------------copy--------------------------");
        //把list3中的元素拷贝到list4中
        //会覆盖原来的元素
        //注意点:如果list3的长度 > list4的长度,方法会报错
        ArrayList<Integer> list3 = new ArrayList<>();
        ArrayList<Integer> list4 = new ArrayList<>();
        Collections.addAll(list3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Collections.addAll(list4, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
        Collections.copy(list4, list3);
        System.out.println(list3);
        System.out.println(list4);

        System.out.println("-------------fill--------------------------");
        //把集合中现有的所有数据,都修改为指定数据
        ArrayList<Integer> list5 = new ArrayList<>();
        Collections.addAll(list5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Collections.fill(list5, 100);
        System.out.println(list5);

        System.out.println("-------------max/min--------------------------");
        //求最大值或者最小值
        ArrayList<Integer> list6 = new ArrayList<>();
        Collections.addAll(list6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println(Collections.max(list6));
        System.out.println(Collections.min(list6));

        System.out.println("-------------max/min指定规则--------------------------");
        // String中默认是按照字母的abcdefg顺序进行排列的
        // 现在我要求最长的字符串
        // 默认的规则无法满足,可以自己指定规则
        // 求指定规则的最大值或者最小值
        ArrayList<String> list7 = new ArrayList<>();
        Collections.addAll(list7, "a","aa","aaa","aaaa");
        System.out.println(Collections.max(list7, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        }));

        System.out.println("-------------swap--------------------------");
        ArrayList<Integer> list8 = new ArrayList<>();
        Collections.addAll(list8, 1, 2, 3);
        Collections.swap(list8,0,2);
        System.out.println(list8);



    }

13.9综合练习

案例:自动点名器1
班级里有N个学生,实现随机点名。

public static void main(String[] args) {
        /*案例:自动点名器1
            班级里有N个学生,实现随机点名     */

        //0.创建集合
        ArrayList<String> list = new ArrayList<>();
        //1.添加数据
        Collections.addAll(list,"张三","李四","王五","范丞丞","韩信","关羽");

        //2.随机点名
        Random r = new Random();
        int index = r.nextInt(list.size());
        String name = list.get(index);
        System.out.println(name);

		//方法2:
        Collections.shuffle(list);
        String s = list.get(0);
        System.out.println(s);
    }

案例:自动点名器2:
班级里有N个学生
要求:
70%的概率随机到男生
30%的概率随机到女生

public static void main(String[] args) {
        /*案例:自动点名器2:
            班级里有N个学生
            要求:
                70%的概率随机到男生
                30%的概率随机到女生*/

        //0.创建集合
        ArrayList<Integer> list = new ArrayList<>();

        //1.添加数据
        Collections.addAll(list,1,1,1,1,1,1,1);
        Collections.addAll(list,0,0,0);

        //2.打乱集合中的数据
        Collections.shuffle(list);

        //3.从list中随机抽取0或1
        Random r = new Random();
        int index = r.nextInt(list.size());
        int num = list.get(index);
        System.out.println(num);

        //4.创建两个集合分别存储男生和女生
        ArrayList<String> boyList = new ArrayList<>();
        ArrayList<String> girlList = new ArrayList<>();

        //5.添加数据
        Collections.addAll(boyList,"范闲","范建","范统","杜子腾","鸠摩智","韩信");
        Collections.addAll(girlList,"小诗诗","小丹丹","小慧慧","黄蓉","猿媛");

        //6.判断并获取姓名
        if (num==1){
            int i = r.nextInt(boyList.size());
            String name = boyList.get(i);
            System.out.println(name);
        }else {
            int i = r.nextInt(girlList.size());
            String name = girlList.get(i);
            System.out.println(name);
        }
    }

案例:自动点名器3:
班级里有N个学生
要求:被点到的学生不会再被点到
但是如果班级中所有的学生都点完了,需要重新开启第二轮点名

 public static void main(String[] args) {
        /*案例:自动点名器3:
            班级里有N个学生
            要求:被点到的学生不会再被点到
            但是如果班级中所有的学生都点完了,需要重新开启第二轮点名*/

        //0.创建集合
        ArrayList<String> list1 = new ArrayList<>();
        //5.创建一个新数组用来存储被删除的元素
        ArrayList<String> list2 = new ArrayList<>();

        //1.添加数据
        Collections.addAll(list1, "范闲", "范建", "范统", "杜子腾", "鸠摩智", "韩信", "小诗诗", "小丹丹", "小慧慧", "黄蓉", "猿媛");
        
        //4. 获取集合长度
        int count = list1.size();
        //2.随机点名
        Random r = new Random();

        //如果点10轮
        for (int j = 1; j <= 10; j++) {
            System.out.println("=====第" + j + "轮点名开始");
            for (int i = 0; i < count; i++) {
                int index = r.nextInt(list1.size());
                /*String name = list.get(index);*/
                //3.被点到的学生不会再被点到
                String name = list1.remove(index);
                list2.add(name);
                System.out.println(name);
            }
            list1.addAll(list2);
            list2.clear();
        }
    }

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

public static void main(String[] args) {
        //0.创建Map集合
        HashMap<String, ArrayList<String>> hm = new HashMap<>();

        //1.创建单列集合
        ArrayList<String> city1 = new ArrayList<>();
        Collections.addAll(city1, "南京市", "扬州市", "苏州市", "无锡市", "常州市");

        ArrayList<String> city2 = new ArrayList<>();
        city2.add("武汉市");
        city2.add("孝感市");
        city2.add("十堰市");
        city2.add("宜昌市");
        city2.add("鄂州市");

        ArrayList<String> city3 = new ArrayList<>();
        Collections.addAll(city3, "石家庄市", "唐山市", "邢台市", "保定市", "张家口市");

        //2.把数据添加到Map集合中
        hm.put("江苏省", city1);
        hm.put("湖北省", city2);
        hm.put("河北省", city3);

        Set<Map.Entry<String, ArrayList<String>>> entries = hm.entrySet();
        for (Map.Entry<String, ArrayList<String>> entry : entries) {
            String key = entry.getKey();
            ArrayList<String> value = entry.getValue();
            StringJoiner sj = new StringJoiner(", ", "", "");
            for (String city : value) {
                sj.add(city);
            }

            System.out.println(key + " = " + sj);
        }

    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1468504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【动态规划专栏】动态规划:似包非包---不同的二叉树

本专栏内容为&#xff1a;算法学习专栏&#xff0c;分为优选算法专栏&#xff0c;贪心算法专栏&#xff0c;动态规划专栏以及递归&#xff0c;搜索与回溯算法专栏四部分。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小…

SmartX 携手 openGauss 社区发布联合方案评测与性能最佳实践

近日&#xff0c;北京志凌海纳科技有限公司&#xff08;以下简称 “SmartX”&#xff09;携手 openGauss 社区完成了 openGauss 数据库基于 SmartX 超融合平台&#xff08;SMTX OS&#xff09;和 SmartX 分布式存储平台&#xff08;SMTX ZBS&#xff09;的性能测试和调优。 结果…

【C++】模板初阶 | 泛型编程 | 函数模板 | 类模板

目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 【本节目标】 1. 泛型编程 2. 函数模板 3. 类模板 1. 泛型编程 如何实现一…

C语言调试

目录 一.Debug和Release介绍 二.Windows环境调试介绍 三.窗口介绍 &#xff08;1&#xff09;自动窗口和局部变量窗口 &#xff08;2&#xff09;监视窗口 &#xff08;3&#xff09;调用堆栈 &#xff08;4&#xff09;查看汇编信息 &#xff08;5&#xff09;查看寄存…

Java零基础 - 算术运算符

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一个人虽可以走的更快&#xff0c;但一群人可以走的更远。 我是一名后…

Win11网络连接选项和蓝牙选项突然消失的解决办法

在设置或者开始栏里搜索“网络重置” 打开网络重置&#xff1a; 然后点击立即重置&#xff0c;之后按照系统提示操作即可

Mybatis总结--传参二

#叫做占位符 Mybatis是封装的JDBC 增强版 内部还是用的jdbc 每遇到一个#号 这里就会变为&#xff1f;占位符 一个#{}就是对应一个问号 一个占位符 用这个对象执行sql语句没有sql注入的风险 八、多个参数-使用Param 当 Dao 接口方法有多个参数&#xff0c;需要通过名称使…

猫头虎分享已解决Bug || 超时错误:TimeoutError: Request timed out after 30000ms.

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

进程1——进程与线程——day09

今天&#xff0c;主要讲一下进程的一些基本概念和一些接口 首先是进程的基本概念&#xff1a; 1.进程: 程序&#xff1a;存放在外存中的一段数据组成的文件 进程&#xff1a;是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡 2.进程相关命令: 1.top 动态…

51单片机项目(34)——基于51单片机和esp8266的智能农业检测系统

1.设计要求 可以检测农业大棚中的温度、湿度、气压、光照等信息&#xff0c;可以检测土壤湿度&#xff0c;可以判断当前有没有下雨&#xff0c;能够将相关数据显示在OLED屏幕上。同时&#xff0c;使用esp8266wifi模块&#xff0c;将上述所有信息发送到手机APP显示。当温度、湿度…

SQL-Labs46关order by注入姿势

君衍. 四十六关 ORDER BY数字型注入1、源码分析2、rand()盲注3、if语句盲注4、时间盲注5、报错注入6、Limit注入7、盲注脚本 四十六关 ORDER BY数字型注入 请求方式注入类型拼接方式GET报错、布尔盲注、延时盲注ORDER BY $id 我们直接可以从界面中得知传参的参数为SORT&#x…

可变参数、Collections类

一、可变参数 定义&#xff1a;是一种特殊的形参&#xff0c;定义在方法、构造器的形参列表里 格式&#xff1a;数据类型...参数名称 特点&#xff1a;可以不传数据&#xff0c;也可以传一个或者多个数据给它&#xff0c;也可以传一个数组 好处&#xff1a;可以灵活接收数据…

刷题日记-Day2- Leedcode-977. 有序数组的平方,209. 长度最小的子数组,59. 螺旋矩阵 II-Python实现

刷题日记Day2 977 有序数组的平方209. 长度最小的子数组59. 螺旋矩阵 II 977 有序数组的平方 链接&#xff1a;https://leetcode.cn/problems/squares-of-a-sorted-array/description/ 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组…

真Unity3D编辑器Editor二次开发

IMGUI Editor Label 改变颜色 分享一个很神奇的颜色 一开始这么写&#xff0c;以为不行的&#xff0c; private void OnGUI()(){GUILayout.Label("<colorred>name:</color>ffdasilufoi");//。。。。 } 结果这么写又好了&#xff0c; private GUIStyle m…

Vivado MIG ip核使用教程

Step 1 在ip catalog中搜索mig ip核并打开&#xff0c;检查硬件配置 Step 2 Step 3 选择对其他芯片类型的兼容性&#xff0c;若无此方面需求&#xff0c;可直接点击next Step 4 选择存储器类型 Step 5 配置DDR3芯片工作频率、用户时钟、mig ip核输入时钟、DDR3芯片类型…

数据结构-Queue队列

一,队列的简单认识 队列也是一种线性数据结构,与栈不同的是,它只能从一端添加元素,从另一端取出元素.定义了一端,另一端也就确定了. (当然还有一个特殊的双向队列LinkedList除外,它既可以从队首添加元素,也可以移除元素,队尾也是一样的,既可以添加元素,也可以移除元素) 二,队…

AI误导游戏——LLM的危险幻觉

在当今科技高速发展的时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为日常生活和工作中不可或缺的一部分。特别是大语言模型&#xff08;LLM&#xff09;如GPT-4等&#xff0c;它们的智能表现令人惊叹&#xff0c;广泛应用于文本生成、语言翻译、情感分析等多个领域…

【rust】9、reqwest 调用 http

文章目录 一、client1.1 post reqwest 实现的 http server 和 client 用 https://github.com/seanmonstar/reqwest cargo add reqwest -F json cargo add tokio -F full一、client 1.1 post async fn http_post<T: Serialize>(addr: String, body: T) -> Result<…

jQuery 基础、选择器和筛选器

【一】JQuery基础 【1】什么时Jquery &#xff08;1&#xff09;定义 jQuery是一个流行的JavaScript库&#xff0c;旨在简化JavaScript编程和处理HTML文档的任务。它提供了一组易于使用的功能和方法&#xff0c;可以加快开发速度并提高跨浏览器兼容性。一款轻量级的JS框架 …

【Java程序设计】【C00285】基于Springboot的游戏分享网站(有论文)

基于Springboot的游戏分享网站&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的游戏分享网站 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xff1a;在网站首页可以查看首页、游戏…