06-java基础——集合的复习

集合的体系结构

  1. 集合主要分为两类:
    • 单列集合
    • 双列集合

一、单列集合

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

  • list系列集合:添加的元素是有序、可重复、有索引的。

    • 有序:指的是存和取的顺序是一致的
  • set系列集合:添加的元素是无序、不可重复、无索引的。

  • collection:collection是一个接口,是单列集合的最顶层接口,它的功能是全部单列集合都可以继承使用。

  • collection:常见的方法:

    • 在这里插入图片描述

collection集合的遍历方式

  1. 迭代器遍历
    • 迭代器遍历是集合的专用遍历方法
    • Iterator中的常用方法
      • boolean hasNext(): 判断当前位置是否有元素可以被取出
      • E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置(E是泛型,你集合存储的数据的类型)
    • 注意点:
      • 1.报错NoSuchElementException(当迭代器的指针遍历完毕后,再使用next方法获取数据就会报错)
      • 2.迭代器遍历完毕,==指针不会复位 ==
      • 3.循环中只能用一次next方法
      • 4.迭代器遍历时,不能用集合的方法进行增加或者删除
      • 5.想要继续第二次遍历集合,只能再次获取一个新的迭代器对象
public class IteratorDemo1 {public static void main(String[] args) {//创建集合对象Collection<String> c = new ArrayList<>();//添加元素c.add("hello");c.add("world");c.add("java");c.add("javaee");//Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到Iterator<String> it = c.iterator();// 获取集合的迭代器//用while循环改进元素的判断和获取while (it.hasNext()) {  // 判断迭代器是否有元素String s = it.next(); // 获取元素并把指针往下传System.out.println(s);}}
}
  1. 增强for循环
    • 增强for循环的底层就是一个迭代器,就是为了简化迭代器的代码书写的
    • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
    • 实现Iterable接口的类才可以使用迭代器和增强for(所有的单列集合和数组才可以使用增强for循环进行遍历)
    • 修改增强for循环中的变量,不会改变集合中原来的数据
public class AddFor {public static void main(String[] args) {/*语法:for(集合/数组中元素的数据类型 变量名 :  集合/数组名) {// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可}*/ArrayList<String> list =  new ArrayList<>();list.add("a");list.add("b");list.add("c");//1,数据类型一定是集合或者数组中元素的类型//2,str仅仅是一个变量名而已,在循环的过程中,依次表示集合或者数组中的每一个元素//3,list就是要遍历的集合或者数组for(String str : list){System.out.println(str); }for(String str : list){str="999";   //这里即时修改了str的值,但是集合里面的数据依旧是原来的数据}System.out.println(list);  //集合的数据不会被修改}
}
  1. lambda表达式
    • 利用forEach方法,再结合lambda表达式的方式进行遍历
      在这里插入图片描述
public class lambda {public static void main(String[] args) {/*lambda表达式遍历:default void forEach(Consumer<? super T> action):*///1.创建集合并添加元素Collection<String> coll = new ArrayList<>();coll.add("zhangsan");coll.add("lisi");coll.add("wangwu");//2.利用匿名内部类的形式//底层原理://其实是通过for循环遍历集合,依次得到每一个元素//然后把得到的每一个元素,传递给下面的accept方法// s依次表示集合中的每一个数据/* coll.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});*///lambda表达式/* coll.forEach((String s) -> {System.out.println(s)});*/
//        简写coll.forEach(s -> System.out.println(s));}
}

一、1、List系列集合

  • List集合的特点

    • 存取有序
    • 可以重复
    • 有索引
  • list集合特有的方法

    • list实现了Collection接口,collection接口的所有方法它都有
      在这里插入图片描述
  • list集合的五种遍历方法:(collection集合的三种+独有的两种)、

      1. 迭代器
      1. 列表迭代器(是迭代器的子接口,额外添加了一个方法:在遍历的过程中,可以添加元素
      1. 增强for
      1. Lambda表达式
      1. 普通for循环
  • list集合删除元素的方式:

    • 1.直接删除元素
    • 2.通过索引进行删除

数据结构

  • 数据结构:计算机存储、组织数据的方式

    • 是指数据相互之间是以什么方式排列在一起的
  • 数据结构是为了更加方便的管理和使用数据,需要结合具体的业务场景来进行选择

  • 常见的数据结构:

    • :后进先出,先进后出

      • 栈的应用场景:java的内存结构——栈内存:先运行的方法最后再出栈
        在这里插入图片描述
    • 队列:先进先出,后进后出;

    • 数组:查询快,增删慢;

      • 每次删除都要把数组的数据前移
      • 每次新增都要把数组的数据后移
    • 链表:链表中的结点是独立的对象,在内存中是不连续的,每个结点(单向链表的结点)包含数据值和下一个结点的地址。(双向链表的结点包含三部分:前一个节点的地址,数据值,下一个结点的地址)

      • 链表查询慢,无论查询哪个数据都要从头开始找
      • 链表增删相对快(相比数组而言)
    • 二叉树

    • 二叉查找树

    • 平衡二叉树

    • 红黑树

ArrayList底层源码分析

  • ArrayLisr的底层数据结构是数组,查询快、增删慢

  • 核心步骤:

    • 第一步:在使用空参创建ArrayList对象的时候,它在底层会先创建一个长度=0的数组;
      • 底层数组的名称:elementDate
      • 变量记录元素的个数的名称:size
        • size的两层含义:元素的个数,下一个元素的存入位置
    • 第二步:添加第一个元素的时候,元素会新的长度=10(10是数组底层的默认长度)的数组
    • 第三步:继续添加元素,添加完毕后,size++;
    • 第四步:当添加的元素存满的时候,就会触发数组的扩容机制
      • 扩容时机一:当数组存满的时候,会创建一个新的数组,新数组的长度=原来的1.5倍,再把原来数组的所有元素全拷贝到新数组中。’
      • 扩容时机二一次性添加多个数据的时候,如果扩容1.5倍数组的容量还不够,那么新创建的数组的长度则以实际的长度为准(即添加后的数组有多少个元素,数组的长度就是多少)
  • 扩容时机1:
    在这里插入图片描述

  • 扩容时机2:
    在这里插入图片描述

LinkedList底层源码分析

  1. linkedList的底层数据结构是双向链表,查询慢,增删相对较快,但是首尾元素的操作的速度快,因为提供了很多首尾操作的特有的API。
    在这里插入图片描述
  2. 底层源码分析:
      1. 刚开始创建的时候,底层创建了两个变量:一个记录头结点first,一个记录尾结点last,默认为null
      1. 添加第一个元素时,底层创建一个结点对象,first和last都记录这个结点的地址值
      1. 添加第二个元素时,底层创建一个结点对象,第一个结点会记录第二个结点的地址值,last会记录新结点的地址值
        在这里插入图片描述
  • 添加第一个元素
    在这里插入图片描述

  • 添加第二个元素
    在这里插入图片描述

  • 添加三个元素在这里插入图片描述

迭代器源码分析:

迭代器遍历相关的三个方法:

  • Iterator iterator() :获取一个迭代器对象

  • boolean hasNext() :判断当前指向的位置是否有元素

  • E next() :获取当前指向的元素并移动指针
    在这里插入图片描述

泛型的使用

  1. 泛型可以在编译阶段约束操作的数据类型,并进行检查

  2. 泛型的格式:<数据类型>

  3. 泛型只能支持引用数据类型

  4. 泛型的好处:

    • 统一数据类型
    • 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来了。
    • java中的泛型是伪泛型
  5. 泛型的细节:

    • 泛型中不可以写基本数据类型(在定义集合的泛型的时候,会把存入的数据的类型由定义的泛型转换成Object类型的,如果是基本数据类型则无法转换成object类型)
    • 指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类型
    • 如果不写泛型,类型默认是Object
  6. 泛型类、泛型方法、泛型接口:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

一、2、set系列集合

数据结构

  • 数据结构:计算机存储、组织数据的方式

    • 是指数据相互之间是以什么方式排列在一起的
  • 数据结构是为了更加方便的管理和使用数据,需要结合具体的业务场景来进行选择

  • 常见的数据结构:

    • :后进先出,先进后出

      • 栈的应用场景:java的内存结构——栈内存:先运行的方法最后再出栈
        在这里插入图片描述
    • 队列:先进先出,后进后出;

    • 数组:查询快,增删慢;

      • 每次删除都要把数组的数据前移
      • 每次新增都要把数组的数据后移
    • 链表:链表中的结点是独立的对象,在内存中是不连续的,每个结点(单向链表的结点)包含数据值和下一个结点的地址。(双向链表的结点包含三部分:前一个节点的地址,数据值,下一个结点的地址)

      • 链表查询慢,无论查询哪个数据都要从头开始找
      • 链表增删相对快(相比数组而言)
    • 二叉树:二叉树中,任意一个节点的度要小于等于2

      • 节点: 在树结构中,每一个元素称之为节点
      • : 每一个节点的子节点数量称之为度
        在这里插入图片描述
        在这里插入图片描述
    • 二叉查找树:二叉查找树,又称二叉排序树或者二叉搜索树

      • 每一个节点上最多有两个子节点
      • 左子树上所有节点的值都小于根节点的值
      • 右子树上所有节点的值都大于根节点的值
        在这里插入图片描述
        在这里插入图片描述
        在这里插入图片描述
    • 平衡二叉树任意一个结点的左右子树高度差不能超过1
      在这里插入图片描述

      • 旋转的触发机制:
        • 当添加一个节点之后,该树不再是一颗平衡二叉树
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
          在这里插入图片描述
    • 左左: 当根节点左子树的左子树有节点插入,导致二叉树不平衡

    • 左右: 当根节点左子树的右子树有节点插入,导致二叉树不平衡

    • 右右: 当根节点右子树的右子树有节点插入,导致二叉树不平衡

    • 右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡 在这里插入图片描述

    • 红黑树:红黑树增删改查性能都很好。
      在这里插入图片描述
      在这里插入图片描述

  1. set系列集合特点:

    • 无序:存取顺序不一致
    • 不重复:可以去除重复的元素
    • 无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
  2. set集合的实现类

    • HashSet:无序、重复、无索引
    • LinkedHashSet:有序、不重复、无索引
    • TreeSet:可排序、不重复、无索引
  • Set接口中的方法基本上与Collection的API一致

Hashset集合

  1. hashSet集合底层数据结构是采取哈希表(数组+链表+红黑树) 存储数据

    • 哈希表是一种对于增删改查性能都较好的数据结构
      • 哈希表的组成:
        • JDK8之前:数组+链表
        • JDK8之后数组+链表+红黑树
  2. 哈希值:

    • 哈希值是根据hashCode方法算出来的int类型的整数
    • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
    • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值
  3. 对象的哈希值特点:

    • 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
    • 如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
    • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可以一样(哈希碰撞)(比如当创建对象的个数大于int类型的范围的时候,超出来的哈希对象的哈希值必定就是重复的)
  4. 底层原理:

    • hashSet的底层是hashMap,第一次添加元素的时候,创建一个默认长度为16的数组,默认的加载因子为0.75,数组名table。
      1. 添加一个元素时,先得到该元素的hash值,然后根据hash值和数组的长度的与运算计算出对应的索引值(元素存储的位置)
      1. 判断当前索引位置的值是否为null:
      • 如果是null就直接存入
      • 如果不是null,表示该位置有元素,则调用equals与该位置的元素进行比较属性值:
        • 属性值一样:放弃添加数据
        • 属性值不一样,存入数组,形成链表,新元素直接挂载老元素的下面
      1. 扩容机制:
      • 第一次添加元素,数组table扩容到16,临界值=16*0.75=12,0.75是加载因子
      • 如果table数组使用到了临界值,数组的大小就会扩容到原来的两倍,新的临界值=16x2x0.75=24,依此类推。
      • 当一条链表的元素个数到达8时,且table数组的大小>=64,就会进行树化(链表直接变成红黑树),否则仍然采用数据的扩容机制。
  • 集合中存储的数组是自定义对象,必须重写hashCode方法和equals方法。

LinkedHashset集合

  1. 底层原理:底层数据结构也是使用哈希表(数组+双向链表),只是每个元素又额外多了一个双链表的机制记录存储的顺序。(底层维护的是一个LinkedHashMap(是HashMap的子类)
    在这里插入图片描述
  • 有序:LinkedHashSet内部使用了一个双向链表来维护元素的插入顺序,因此遍历LinkedHashSet时,元素的顺序将与它们的插入顺序一致。

TreeSet集合

  1. TreeSet的底层是基于红黑树的数据结构实现排序的,增删改查性能都很好
public class TreeSet_ {public static void main(String[] args) {//解读//1. 当我们使用无参构造器,创建TreeSet时,仍然是无序的//2. 如果希望添加的元素,按照字符串大小来排序//3. 使用TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)//   并指定排序规则//4. 简单看看源码//解读/*1. 构造器把传入的比较器对象,赋给了 TreeSet的底层的 TreeMap的属性this.comparatorpublic TreeMap(Comparator<? super K> comparator) {this.comparator = comparator;}2. 在 调用 treeSet.add("tom"), 在底层会执行到if (cpr != null) {//cpr 就是我们的匿名内部类(对象)do {parent = t;//动态绑定到我们的匿名内部类(对象)comparecmp = cpr.compare(key, t.key);if (cmp < 0)t = t.left;else if (cmp > 0)t = t.right;else //如果相等,即返回0,这个Key就没有加入return t.setValue(value);} while (t != null);}*///        TreeSet treeSet = new TreeSet();TreeSet treeSet = new TreeSet(new Comparator() {@Overridepublic int compare(Object o1, Object o2) {//下面 调用String的 compareTo方法进行字符串大小比较//如果老韩要求加入的元素,按照长度大小排序//return ((String) o2).compareTo((String) o1);return ((String) o1).length() - ((String) o2).length();}});//添加数据.treeSet.add("jack");treeSet.add("tom");//3treeSet.add("sp");treeSet.add("a");treeSet.add("abc");//如果是按照长度大小的规则来比较的,这个abc是加不进去的,因为上面的tom的长度也是3.(总的来说就是:return ((String) o1).length() - ((String) o2).length();如果return的值为0,那么这个key就加入不了。System.out.println("treeSet=" + treeSet);}
}

在这里插入图片描述

二、双列集合

  1. 双列集合的特点:用于报错具有映射关系的数据:key-value
      1. 双列集合一次需要存一对数据,分别是键和值(k,v)
      1. 键(key)不能重复值(value)可以重复
      1. 双列集合中的key可以为null,value也可以为null,key为null只有一个,value为null可以有多个。
      1. 键和值是一一对应的,每一个键只能找到自己对应的值
      1. 键+值这个整体我们称之为“键值对”或者“键值对对象“,在java中叫做”Entry对象”。

在这里插入图片描述

Map系列集合常见的API

在这里插入图片描述

  • map集合的获取功能
    在这里插入图片描述
  • map集合的遍历方法1:使用keySet()
public class MapDemo01 {public static void main(String[] args) {//创建集合对象Map<String, String> map = new HashMap<String, String>();//添加元素map.put("张无忌", "赵敏");map.put("郭靖", "黄蓉");map.put("杨过", "小龙女");//获取所有键的集合。用keySet()方法实现Set<String> keySet = map.keySet();//遍历键的集合,获取到每一个键。用增强for实现for (String key : keySet) {//根据键去找值。用get(Object key)方法实现String value = map.get(key);System.out.println(key + "," + value);}}
}
  • map集合的遍历方法2:Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
public class MapDemo02 {public static void main(String[] args) {//创建集合对象Map<String, String> map = new HashMap<String, String>();//添加元素map.put("张无忌", "赵敏");map.put("郭靖", "黄蓉");map.put("杨过", "小龙女");//获取所有键值对对象的集合Set<Map.Entry<String, String>> entrySet = map.entrySet();//遍历键值对对象的集合,得到每一个键值对对象for (Map.Entry<String, String> me : entrySet) {//根据键值对对象获取键和值String key = me.getKey();String value = me.getValue();System.out.println(key + "," + value);}}
}

HashMap源码解读

/*
1.看源码之前需要了解的一些内容Node<K,V>[] table   哈希表结构中数组的名字DEFAULT_INITIAL_CAPACITY:   数组默认长度16DEFAULT_LOAD_FACTOR:        默认加载因子0.75HashMap里面每一个对象包含以下内容:
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;//把哈希表中数组的地址值,赋值给局部变量tabtab = table;if (tab == null || (n = tab.length) == 0){//1.如果当前是第一次添加数据,底层会创建一个默认长度为16,加载因子为0.75的数组//2.如果不是第一次添加数据,会看数组中的元素是否达到了扩容的条件//如果没有达到扩容条件,底层不会做任何操作//如果达到了扩容条件,底层会把数组扩容为原先的两倍,并把数据全部转移到新的哈希表中tab = resize();//表示把当前数组的长度赋值给nn = 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//如果键一样,返回trueboolean 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   555if (e != null) {V oldValue = e.value;if (!onlyIfAbsent || oldValue == null){//等号的右边:当前要添加的值//等号的左边:0x0044的值e.value = value;}afterNodeAccess(e);return oldValue;}}//threshold:记录的就是数组的长度 * 0.75,哈希表的扩容时机  16 * 0.75 = 12if (++size > threshold){resize();}//表示当前没有覆盖任何元素,返回nullreturn 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) {//获取根节点的地址值,赋值给局部变量tEntry<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;elseparent.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

三、Collections工具类

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

public class Collections_ {public static void main(String[] args) {//创建ArrayList 集合,用于测试.List list = new ArrayList();list.add("tom");list.add("smith");list.add("king");list.add("milan");list.add("tom");//        reverse(List):反转 List 中元素的顺序Collections.reverse(list);System.out.println("list=" + list);
//        shuffle(List):对 List 集合元素进行随机排序
//        for (int i = 0; i < 5; i++) {
//            Collections.shuffle(list);
//            System.out.println("list=" + list);
//        }//        sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序Collections.sort(list);System.out.println("自然排序后");System.out.println("list=" + list);
//        sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序//我们希望按照 字符串的长度大小排序Collections.sort(list, new Comparator() {@Overridepublic int compare(Object o1, Object o2) {//可以加入校验代码.return ((String) o2).length() - ((String) o1).length();}});System.out.println("字符串长度大小排序=" + list);
//        swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换//比如Collections.swap(list, 0, 1);System.out.println("交换后的情况");System.out.println("list=" + list);//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素System.out.println("自然顺序最大元素=" + Collections.max(list));//Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素//比如,我们要返回长度最大的元素Object maxObject = Collections.max(list, new Comparator() {@Overridepublic int compare(Object o1, Object o2) {return ((String)o1).length() - ((String)o2).length();}});System.out.println("长度最大的元素=" + maxObject);//Object min(Collection)//Object min(Collection,Comparator)//上面的两个方法,参考max即可//int frequency(Collection,Object):返回指定集合中指定元素的出现次数System.out.println("tom出现的次数=" + Collections.frequency(list, "tom"));//void copy(List dest,List src):将src中的内容复制到dest中ArrayList dest = new ArrayList();//为了完成一个完整拷贝,我们需要先给dest 赋值,大小和list.size()一样for(int i = 0; i < list.size(); i++) {dest.add("");}//拷贝Collections.copy(dest, list);System.out.println("dest=" + dest);//boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值//如果list中,有tom 就替换成 汤姆Collections.replaceAll(list, "tom", "汤姆");System.out.println("list替换后=" + list);}
}

四、集合总结

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python爬虫实战案例——王者荣耀皮肤抓取

大家好&#xff0c;我是你们的老朋友——南枫&#xff0c;今天我们一起来学习一下该如何抓取大家经常玩的游戏——王者荣耀里面的所有英雄的皮肤。 老规矩&#xff0c;直接上代码&#xff1a; 导入我们需要使用到的&#xff0c;也是唯一用到的库&#xff1a; 我们要抓取皮肤其…

统计信号处理基础 习题解答11-11

题目 考虑矢量MAP估计量 证明这个估计量对于代价函数 使贝叶斯风险最小。其中&#xff1a;, &#xff0c;且. 解答 贝叶斯风险函数&#xff1a; 基于概率密度的非负特性&#xff0c;上述对积分要求最小&#xff0c;那就需要内层积分达到最小。令内层积分为&#xff1a; 上述积…

【SkiaSharp绘图12】SKCanvas方法详解(一)清空、裁切区域设置、连接矩阵、注释、弧与扇形、图集、九宫格绘图、圆

文章目录 SKCanvas 方法Clear 清空ClipPath/ClipRect/ClipRegion/ClipRoundRect 设置裁切区域Concat 连接矩阵DrawAnnotation绘制注释DrawArc绘制椭圆弧、扇形DrawAtlas绘制图集(一个图像、多个区域、多个缩放、一次绘制&#xff09;DrawBitmap绘制图像DrawBitmapNinePatch九宫…

停车场车牌识别计费系统,用Python如何实现?

关注星标&#xff0c;每天学习Python新技能 前段时间练习过的一个小项目&#xff0c;今天再看看&#xff0c;记录一下~ 项目结构 说明&#xff1a; datefile文件夹&#xff1a;保存车辆信息表的xlsx文件 file文件夹&#xff1a;保存图片文件夹。ic_launcher.jpg是窗体的右上角…

vector模拟实现【C++】

文章目录 全部的实现代码放在了文章末尾准备工作包含头文件定义命名空间和类类的成员变量 迭代器迭代器获取函数 构造函数默认构造使用n个值构造迭代器区间构造解决迭代器区间构造和用n个值构造的冲突拷贝构造 析构函数swap【交换函数】赋值运算符重载emptysize和capacityopera…

字符串知识点

API API和API帮助文档 API:目前是JDK中提供的各种功能的Java类。 这些类将底层的实现封装了起来&#xff0c;我们不需要关心这些类是如何实现的&#xff0c;只需要学习这些类如何使用即可。 API帮助文档&#xff1a;帮助开发人员更好的使用API和查询API的一个工具。 String概…

【Linux】线程封装与互斥(万字)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 文章目录 前言 C多线程的用法 对原生线程进行一次封装 理解pthread线程 Linux线程互斥 进程线程间的互斥相关背景概念 互斥量mutex 操作共享变量会有问题的售票…

eventloop 事件循环机制 (猜答案)

// eventloop 事件循环机制// console.log(555);setTimeout(() > {console.log(666);})let p new Promise((resolve,reject)>{// 同步执行console.log(111);resolve();});// promise 的回调函数是异步的微任务p.then(v > {console.log(222);}, r > {console.log(r…

解决ps暂存盘已满的问题

点击编辑->首选项->暂存盘 ps默认暂存盘使用的是c盘&#xff0c;我们改成d盘即可 然后重启ps

OpenSSL的一些使用案例

目录 一、介绍 二、基本使用 1、Shell &#xff08;1&#xff09;文件加解密 &#xff08;2&#xff09;生成密钥文件 2、API &#xff08;1&#xff09;md5sum &#xff08;2&#xff09;AES256加解密 一、介绍 本篇博客重点不是详细描述 OpenSSL 的用法&#xff0c;只…

snap和apt的区别简单了解

Linux中没有tree命令的时候提示安装的时候出现了两个命令&#xff0c;简单看了看两者有何区别&#xff08;一般用apt就可以了&#xff09;&#xff1a; sudo snap install tree 和 sudo apt install tree 这两个命令都是用来安装 tree 命令行工具的&#xff0c;但它们使用的是不…

antfu/ni 在 Windows 下的安装

问题 全局安装 ni 之后&#xff0c;第一次使用会有这个问题 解决 在 powershell 中输入 Remove-Item Alias:ni -Force -ErrorAction Ignore之后再次运行 ni Windows 11 下的 Powershell 环境配置 可以参考 https://github.com/antfu-collective/ni?tabreadme-ov-file#how …

SpringBoot源码阅读3-启动原理

SpringBootApplication public class DistApplication {public static void main(String[] args) {// 启动入口SpringApplication.run()SpringApplication.run(DistApplication.class, args);} }1、服务构建 这里"服务"指的是SpringApplication对象&#xff0c;服务…

【QT】概述|对象树模型|两种控件模式|信号和槽|lambda

目录 什么是QT 特点 QT程序 main函数 QT按钮 纯代码模式 图形化模式 对象树模型 信号和槽 连接与断开 自动连接 断开连接 信号的发射 lambda表达式 基本语法 捕获列表 Lambda表达式用于信号与槽的连接 例如 什么是QT Qt是一个跨平台的C图形用户界面应用…

WAF的新选择,雷池 SafeLine-安装动态防护使用指南

什么是 WAF WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。 区别于传统防火墙&#xff0c;WAF 工作在应用层&#xff0c;对基于 HTTP/HTTPS 协议的 Web 系统有着更好的防护效果&#xff0c;使其免于受到黑客的攻击&#xff1b; 通俗来讲&#…

【数据库】Oracle安装报错(win10安装oracle提示环境不满足最低要求)

目录 一、问题场景&#xff1a; 二、问题描述 三、原因分析&#xff1a; 四、解决方案&#xff1a; 一、问题场景&#xff1a; 安装Oracle数据库 二、问题描述 安装之前提示&#xff08; [INS-13001]环境不满足最低要求。 是否确实要继续? &#xff09; 如图所示&…

运维锅总浅析云原生DevOps工具

本文从Tekton与Kubevela、Jenkins、GitLab CI的区别与联系对常见的云原生DevOps工具进行对比分析&#xff0c;最后给出DevOps工具选型思路。希望对您有所帮助&#xff01; 一、DevOps简介 DevOps是一种结合了软件开发&#xff08;Development&#xff09;和IT运维&#xff08…

1、项目基础

1、系统架构图 2、项目业务组成 3、技术选型 3.1 前端 vue3 ts sass axios 3.2后端 spring-cloud系列 gateway openfeign spring-cloud-alibaba系列 nacos sentinel seata

开放式耳机怎么选?五大2024年口碑销量爆棚机型力荐!

在选购开放式耳机的时候&#xff0c;我们总会因为有太多的选择而陷入两难&#xff0c;又想要一个颜值比较高的&#xff0c;又想要同时兼顾性能还不错的&#xff0c;所以作为测评博主&#xff0c;今天我们就给大家带来自己的一些选购技巧和自己觉得还不错开放式耳机&#xff0c;…

TP8/6 子域名绑定应用

原www.xxx.com/admin改为admincms.xxx.com config/app.php