JavaSE-13笔记【集合2(+2024新)】

文章目录

  • 3.Map
    • 3.1 Map继承结构
    • 3.2 Map接口的常用方法
    • 3.3 遍历Map
    • 3.4 HashMap集合
      • 3.4.1 HashMap集合key的特点
      • 3.4.2 HashMap集合的key存储自定义类型
      • 3.4.3 哈希表
        • 3.4.3.1 哈希表的介绍
        • 3.4.3.2 哈希表的存储原理
      • 3.4.4 存放在HashMap和HashSet集合key部分的元素必须同时重写hashCode+equals方法,若equals返回true时,hashCode必须相同。
      • 3.4.5 HashMap中的key可以存null
      • 3.4.6 手写HashMap的put和get方法
      • 3.4.7 HashMap在Java8后的改进(包含Java8)
      • 3.4.8 HashMap初始化容量永远都是2的次幂
      • 3.4.9 HashMap的初始化容量的设置
    • 3.5 LinkedHashMap
    • 3.6 Hashtable
    • 3.7 Properties
    • 3.8 一些树的介绍
      • 3.8.1 二叉树
      • 3.8.2 排序二叉树
      • 3.8.3 平衡二叉树(AVL)
      • 3.8.4 红黑二叉树
    • 3.9 TreeMap
      • 3.9.1 TreeMap集合的key为自定义类型
  • 4.Set
    • 4.1 HashSet
    • 4.2 LinkedHashSet
    • 4.3 TreeSet
    • 4.4 关于HashSet的面试题
  • 5.Collections工具类

接上一篇 JavaSE-12笔记【集合1】

3.Map

3.1 Map继承结构

在这里插入图片描述

  • Map集合以key和value的键值对形式存储。key和value存储的都是引用。
  • Map集合中key起主导作用。value是附属在key上的。
  • SequencedMap是Java21新增的。
  • LinkedHashMap和TreeMap都是有序集合。(key是有序的)
  • HashMap,Hashtable,Properties都是无序集合。(key是无序的)
  • Map集合的key都是不可重复的。key重复的话,value会覆盖。
  • HashSet集合底层是new了一个HashMap。往HashSet集合中存储元素实际上是将元素存储到HashMap集合的key部分。HashMap集合的key是无序不可重复的,因此HashSet集合就是无序不可重复的。HashMap集合底层是哈希表/散列表数据结构,因此HashSet底层也是哈希表/散列表。
  • TreeSet集合底层是new了一个TreeMap。往TreeSet集合中存储元素实际上是将元素存储到TreeMap集合的key部分。TreeMap集合的key是不可重复但可排序的,因此TreeSet集合就是不可重复但可排序的。TreeMap集合底层是红黑树,因此TreeSet底层也是红黑树。它们的排序通过java.lang.Comparable和java.util.Comparator均可实现。
  • LinkedHashSet集合底层是new了一个LinkedHashMap。LinkedHashMap集合只是为了保证元素的插入顺序,效率比HashSet低,底层采用的哈希表+双向链表实现。
  • 根据源码可以看到向Set集合中add时,底层会向Map中put。value只是一个固定不变的常量,只是起到一个占位符的作用。主要是key。

3.2 Map接口的常用方法

V put(K key, V value); 添加键值对
void putAll(Map<? extends K,? extends V> m); 添加多个键值对
V get(Object key); 通过key获取value
boolean containsKey(Object key); 是否包含某个key(底层会调用equals方法)
boolean containsValue(Object value); 是否包含某个value(底层会调用equals方法)
V remove(Object key); 通过key删除key-value
void clear(); 清空Map
int size(); 键值对个数
boolean isEmpty(); 判断是否为空Map
Collection values(); 获取所有的value
Set keySet(); 获取所有的key
Set<Map.Entry<K,V>> entrySet(); 获取所有键值对的Set视图。
static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3); 静态方法,使用现有的key-value构造Map

示例代码:

package maptest;import java.util.Collection;
import java.util.HashMap;
import java.util.Map;public class MapTest01 {public static void main(String[] args) {//创建一个Map集合Map<Integer, String> maps = new HashMap<>();//添加键值对maps.put(1,"赵倩");maps.put(2,"王雷");maps.put(3, "张芝");System.out.println("键值对个数:" + maps.size());//根据key获取valueSystem.out.println(maps.get(2));//key对应的value不存在时返回nullSystem.out.println(maps.get(4));//判断是否包含某个keySystem.out.println(maps.containsKey(3));//判断是否包含某个valueSystem.out.println(maps.containsValue("王雷"));//通过key删除键值对maps.remove(3);System.out.println("键值对个数:" + maps.size());//添加多个键值对Map<Integer,String> newMap = new HashMap<>();newMap.put(4,"刘柳");newMap.putAll(maps);//获取所有的valueCollection<String> values = newMap.values();for (String value:values) {System.out.println(value);}System.out.println("当前是否为空map:" + maps.isEmpty());//清空mapmaps.clear();System.out.println("当前是否为空map:" + maps.isEmpty());}
}

运行结果:
在这里插入图片描述

3.3 遍历Map

  • 方式一:通过keySet()方法获取所有key,之后再通过key获取对应value
package maptest;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;public class MapTest02 {public static void main(String[] args) {//创建一个Map集合Map<Integer, String> maps = new HashMap<>();//添加键值对maps.put(1,"赵倩");maps.put(2,"王雷");maps.put(3, "张芝");maps.put(4, "刘柳");//方式一:通过keySet()方法获取所有key,之后再通过key获取对应valueSet<Integer> keys = maps.keySet();Iterator<Integer> it = keys.iterator();while (it.hasNext()){Integer key = it.next();String value = maps.get(key);System.out.println(key + ":" + value);}System.out.println("========================");//使用for-eachSet<Integer> keys1 = maps.keySet();for (Integer key: keys1) {System.out.println(key + ":" + maps.get(key));}}
}

运行结果: 在这里插入图片描述

  • 方式二:通过EntrySet()方法直接获取key和value(这种方式效率较高,推荐使用)
package maptest;import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;public class MapTest02 {public static void main(String[] args) {//创建一个Map集合Map<Integer, String> maps = new HashMap<>();//添加键值对maps.put(1,"赵倩");maps.put(2,"王雷");maps.put(3, "张芝");maps.put(4, "刘柳");//方式二:通过EntrySet()方法Set<Map.Entry<Integer, String>> entries = maps.entrySet();Iterator<Map.Entry<Integer, String>> it = entries.iterator();while (it.hasNext()){Map.Entry<Integer, String> entry = it.next();Integer key = entry.getKey();String value = entry.getValue();System.out.println(key + ":" + value);}System.out.println("================================");//for-eachSet<Map.Entry<Integer, String>> entries2 =  maps.entrySet();for (Map.Entry<Integer, String> entry: entries2) {System.out.println(entry.getKey() + ":" + entry.getValue());}}
}

运行结果:
在这里插入图片描述

3.4 HashMap集合

3.4.1 HashMap集合key的特点

key是无序不可重复的。
无序:存进去的顺序与取出的顺序不一定相同;
不可重复:具有唯一性(如果key重复的话,value会覆盖)。

示例代码:

package maptest;import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class HashMapTest01 {public static void main(String[] args) {Map<Integer, String> map = new HashMap<Integer, String>();map.put(111111115,"张三");map.put(111111112,"李四");map.put(111111113,"王五");map.put(111111114,"赵六");map.put(111111114,"赵六2"); //存储重复的key,value会覆盖Set<Map.Entry<Integer, String>> entries = map.entrySet();for (Map.Entry<Integer, String> entry:entries) { //取出的顺序不一定与存入的顺序相同Integer key = entry.getKey();String value = entry.getValue();System.out.println(key + ":" + value);}}
}

运行结果:
在这里插入图片描述

3.4.2 HashMap集合的key存储自定义类型

自定义User类型(已覆写toString方法和equals方法):

package maptest;import java.util.Objects;public class User {private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if(o == null){return false;}if(o == this){return true;}if(o instanceof User){User user = (User) o;return user.age == this.age && user.name.equals(this.name);}return false;}
}

创建相应的map并遍历:

package maptest;import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class HashMapTest02 {public static void main(String[] args) {//key中存储自定义类型UserMap<User, String> map = new HashMap<User, String>();//创建User对象User user1 = new User("张三", 18);User user2 = new User("李四", 23);User user3 = new User("王五", 25);User user4 = new User("张三", 18);//向map中存入元素map.put(user1,"1111");map.put(user2,"2222");map.put(user3,"3333");map.put(user4,"4444");//遍历mapSet<User> users = map.keySet();for (User user:users) {String value = map.get(user);System.out.println(user + "=" + value);}}
}

运行结果:
在这里插入图片描述
在重写了equals()方法的情况下,对于equals()为true的User作为key,其value并没有被覆盖,而是都存入了map,这与HashMap的存储结构【哈希表】有关。

3.4.3 哈希表

3.4.3.1 哈希表的介绍

HashMap的底层为散列表(又称哈希表)的存储结构【数组+单向链表】,每一个节点存储有四个值:key、hash(哈希值,或者叫哈希码,由key调用hashCode()方法获得)、value、next(下一个节点的地址)
①哈希表是一种查询和增删效率都很高的一种数据结构,非常重要,在很多场合使用,并且面试也很常见。必须掌握。
②哈希表的查询和增删效率都好,因为哈希表是“数组 + 链表”的结合体,同时结合了二者的优点。
③数组和链表的结合不是绝对的。哈希表可能是:数组 + 链表,数组 + 红黑树, 数组 + 链表 + 红黑树等。

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

3.4.3.2 哈希表的存储原理

①哈希函数:

  • 通过哈希函数(即hashCode())可以将一个Java对象映射为一个数字,这个数字就是哈希值。(就像现实世界中,每个人(对象)都会映射一个身份证号(哈希值)一样。)
  • 也就是说hashCode()方法的返回值就是哈希值。
  • 一个好的哈希函数,可以让散列分布均匀。
    ②哈希值:也叫做哈希码。是哈希函数执行的结果。
    ③哈希碰撞:也叫做哈希冲突。
  • 当两个对象“哈希值%数组长度”之后得到的下标相同时,就发生了哈希冲突。
  • 如何解决哈希冲突?将冲突的挂到同一个链表上或同一个红黑树上。
    ④以上描述凡是“哈希”都可以换为“散列”。
    在这里插入图片描述
    哈希表的put()方法的底层执行步骤:(如上图)
  1. 对应的key调用hashCode()方法,得到哈希值(即Node中的hash);
  2. 通过“哈希值%数组长度”得到该键值对的存入数组的索引值(这里可能产生哈希碰撞);
  3. 把键值对存入对应索引的位置,对应两种情况(假设计算的索引值为9):
    情况一:如果索引为9的位置没有存储元素,则将键值对封装为Node对象,然后存入到索引为9的位置中。
    情况二:如果索引为9的位置有存储元素(有首节点),那么就遍历整个单链表,如果遍历出来节点的key和添加键值对的key相同,则做覆盖操作;如果单链表遍历出来节点的key和添加键值对的key都不同,则把添加键值对封装为Node对象,最后插入到单链表的末尾。

哈希表的get()方法的底层执行步骤:

  1. 对应的key调用hashCode()方法,得到哈希值(即Node中的hash);
  2. 通过“哈希值%数组长度”得到该键值对的存入数组的索引值;
  3. 从对应数组索引值的位置的链表中逐一开始查找,直到找到key.equals返回true的节点,返回其value值。

3.4.4 存放在HashMap和HashSet集合key部分的元素必须同时重写hashCode+equals方法,若equals返回true时,hashCode必须相同。

再回去想想刚刚哪个例子,在重写了equals()方法的情况下,对于equals()为true的User作为key,其value并没有被覆盖,而是都存入了map,原因是:并没有调用equals()方法,由于并没有重写hashCode()方法,所以这里两个User的哈希值肯定不一样,而刚好“哈希值%数组长度”得到的索引值也不一样,导致没有产生哈希碰撞,直接就存进去了。

package maptest;import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class HashMapTest02 {public static void main(String[] args) {//key中存储自定义类型UserMap<User, String> map = new HashMap<User, String>();//创建User对象User user1 = new User("张三", 18);User user2 = new User("李四", 23);User user3 = new User("王五", 25);User user4 = new User("张三", 18);//向map中存入元素map.put(user1,"1111");map.put(user2,"2222");map.put(user3,"3333");map.put(user4,"4444");System.out.println("user1的哈希值:" + user1.hashCode());System.out.println("user2的哈希值:" + user2.hashCode());//遍历mapSet<User> users = map.keySet();for (User user:users) {String value = map.get(user);System.out.println(user + "=" + value);}}
}

运行结果:
在这里插入图片描述

所以,重要原理:
①存放在HashMap和HashSet 集合key部分的元素必须同时重写hashCode+equals方法。
②equals返回true时,hashCode的返回值必须相同。

为什么要同时重写hashCode+equals方法?

  • 若equals方法返回true,则表明两个对象为同一个对象,而HashMap(HashSet)中的key是不可重复的,所以两个对象只能放其中一个,如果此时hashCode方法不重写则以对象的内存地址为基础产生哈希值,得到的哈希值一定不相同,则可能不会产生哈希碰撞,使得两个对象都成功存入,如此,则违背了“HashMap(HashSet)中的key是不可重复的”这一原则。

在IDEA中,直接“Alt+Insert”重写hashCode+equals方法即可,非常方便且高效,不需要自己手动重写!
User修改如下:

package maptest;import java.util.Objects;public class User {private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return age == user.age &&Objects.equals(name, user.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}

新运行结果:
在这里插入图片描述

3.4.5 HashMap中的key可以存null

  • HashMap中的key可以存null,但也只能存一个key为null的(key不重复);
  • 如果添加的键值对key为null,则程序默认将该键值对存储到table数组中索引为0的位置。
package maptest;import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class HashMapTest03 {public static void main(String[] args) {//key中存储自定义类型UserMap<User, String> map = new HashMap<User, String>();//存入key为null的节点map.put(null, "11111");map.put(null, "22222");map.put(null, "33333");Set<Map.Entry<User, String>> entries = map.entrySet();for (Map.Entry<User, String> entry:entries) {User key = entry.getKey();String value = entry.getValue();System.out.println(key + ":" + value);}}
}

运行结果:
在这里插入图片描述

3.4.6 手写HashMap的put和get方法

package maptest;/*** 手写HashMap的put和get方法*/
public class MyHashMap<K,V> {/*** 哈希表*/private Node<K,V>[] table;/*** 集合中的键值对个数*/private int size;public MyHashMap() {//注意:这里new数组的时候不能使用泛型,new Node<K,V>[16]这样写是错误的this.table = new Node[16];}static class Node<K,V>{/*** key*/K key;/*** 哈希值*/int hash;/*** value*/V value;/*** 下一个节点的内存地址*/Node<K,V> next;/*** 构造一个节点对象* @param key 键* @param hash 哈希值* @param value 值* @param next 下一个节点的内存地址*/public Node(K key, int hash, V value, Node<K, V> next) {this.key = key;this.hash = hash;this.value = value;this.next = next;}@Overridepublic String toString() {return "{" +"key=" + key +", value=" + value +"}\n";}}/*** 获取集合中键值对个数* @return 个数*/public int size(){return size;}/*** 向MyHashMap中添加一个键值对* @param key 键* @param value 值* @return value,如果key重复,返回的是oldValue,否则返回newValue*/public V put(K key, V value){/*【第一步】:处理key为null的情况如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置。【第二步】:获得key对象的哈希值如果添加键值对的key不是null,则就调用key的hashcode()方法,获得key的哈希值。【第三步】:获得键值对的存储位置因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。【第四步】:将键值对添加到table数组中当table[i]返回结果为null时,则键键值对封装为Node对象并存入到table[i]的位置。当table[i]返回结果不为null时,则意味着table[i]存储的是单链表。我们首先遍历单链表,如果遍历出来节点的key和添加键值对的key相同,那么就执行覆盖操作;如果遍历出来节点的key和添加键值对的key都不同,则就将键键值对封装为Node对象并插入到单链表末尾。*///第一步:处理key为null的情况if(key == null){return putForKeyNull(key,value);}//第二步:获得key对象的哈希值int hash = key.hashCode();//第三步:获得键值对的存储位置int index = Math.abs(hash % table.length);//第四步:将键值对添加到table数组中Node<K,V> node = table[index]; //取出下标index位置的nodeif(node == null){//下标为index的位置上为空table[index] = new Node<>(key, hash, value, null);size++;return value;}//下标为index的位置上有单向链表Node<K,V> prev = null;while (node != null){//找到key等于当前传入key的节点if(node.key.equals(key)){V oldValue = node.value;node.value = value;return oldValue;}prev = node;node = node.next;}//没有找到key等于当前传入key的节点(插入,尾插法)prev.next = new Node<>(key,hash,value,null);size++;return value;}/*** 处理添加key为null的键值对* @param key* @param value* @return*/private V putForKeyNull(K key, V value) {Node<K,V> node = table[0];if(node == null){//下标为0的位置上为空table[0] = new Node<>(key,0,value,null);size++;return value;}//下标为0的位置上有单向链表Node<K,V> prev = null;while (node != null){if(node.key == null){//找到key为null的节点V oldValue = node.value;node.value = value;return oldValue;}prev = node;node = node.next;}//没有找到key为null的节点(插入,尾插法)prev.next = new Node<>(key,0,value,null);size++;return value;}/*** 根据key获取对应value* @param key 键* @return 值*/public V get(K key){/*【第一步】:处理key为null的情况如果查询的key就是null,则就在table数组索引为0的位置去查询。【第二步】:获得key对象的哈希值如果查询的key不是null,则就调用key的hashcode()方法,获得key的哈希值。【第三步】:获得键值对的存储位置因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。【第四步】:遍历单链表,根据key获得value值如果table[i]返回的结果为null,则证明单链表不存在,那么返回null即可如果table[i]返回的结果不为null时,则证明单链表存在,那么就遍历整个单链表。如果遍历出来节点的key和查询的key相同,那么就返回遍历出来节点的value值;如果整个单链表遍历完毕,则遍历出来节点的key和查询的key都不相等,那么就证明查询key在链表中不存在,则直接返回null即可。*///第一步:处理key为null的情况if(key == null){Node<K,V> node = table[0];//索引为0的位置没有存储任何元素if(node == null){return null;}//索引为0的位置存储了元素,有单向链表while (node!= null){if(node.key == null){return node.value;}node = node.next;}}//key不是null//第二步:获得key对象的哈希值int hash = key.hashCode();//第三步:获得键值对的存储位置int index = Math.abs(hash % table.length);//第四步:遍历单链表,根据key获得value值Node<K,V> node = table[index];if(node == null){//下标为index的位置上为空return null;}//下标为index的位置上有单向链表while (node != null){if(node.key.equals(key)){return node.value;}node = node.next;}return null;}/*** 重写toString方法,输出所有节点*/@Overridepublic String toString() {StringBuilder stb = new StringBuilder();for (int i = 0; i < table.length; i++) {Node<K,V> node = table[i];if(node != null){//如果node不为空,遍历整个单向链表while (node != null){stb.append(node);node = node.next;}}}return stb.toString();}
}

测试程序(User与上面最终的User一致):

package maptest;public class HashMapTest04 {public static void main(String[] args) {MyHashMap<Integer,String> map = new MyHashMap<>();map.put(1111, "张三");map.put(2222, "李四");map.put(3333, "王五");map.put(4444, "赵六");map.put(1111, "张三2");map.put(null, "钱七");map.put(null, "钱七2");map.put(null, "钱七3");System.out.println(map);System.out.println(map.get(1111));//key为自定义类型UserMyHashMap<User,String> userMap = new MyHashMap<>();User user1 = new User("张三", 18);User user2 = new User("李四", 23);User user3 = new User("王五", 25);User user4 = new User("张三", 18);userMap.put(user1, "111");userMap.put(user2, "222");userMap.put(user3, "333");userMap.put(user4, "444");userMap.put(user1, "aaa");System.out.println(userMap);System.out.println(userMap.get(user1));}
}

运行结果:
在这里插入图片描述

3.4.7 HashMap在Java8后的改进(包含Java8)

①初始化时机:

  • Java8之前,构造方法执行时初始化table数组。
  • Java8之后,第一次调用put方法时初始化table数组。
    ②插入法:
  • Java8之前,头插法
  • Java8之后,尾插法
    ③数据结构:
  • Java8之前:数组 + 单向链表
  • Java8之后:数组 + 单向链表 + 红黑树。
  • 最开始使用单向链表解决哈希冲突。如果结点数量 >= 8,并且table的长度 >= 64。单向链表转换为红黑树。
  • 当删除红黑树上的结点时,结点数量 <= 6 时。红黑树转换为单向链表。
    -在这里插入图片描述

3.4.8 HashMap初始化容量永远都是2的次幂

在这里插入图片描述
①HashMap集合初始化容量16(第一次调用put方法时初始化)
②HashMap集合的容量永远都是2的次幂,假如给定初始化容量为31,它底层也会变成32的容量。
③将容量设置为2的次幂作用是:1)加快哈希计算;2)减少哈希冲突。

  • 为什么会加快哈希计算?
    首先,使用二进制运算是最快的。当一个数字是2的次幂时,例如数组的长度是2的次幂时,hash & (length-1) 的结果和 hash % length的结果相同。
    注意: 只有length是2的次幂时,以上等式才会成立。因为使用 & 运算符,让效率提升,因此建议容量一直是2的次幂。
  • 为什么会减少哈希冲突?
    计算索引的底层运算是:hash & length - 1。
    如果length是偶数:length-1后一定是奇数,奇数二进制位最后一位一定是1,1和其他二进制位进行与运算,结果可能是1,也可能是0,这样可以减少哈希冲突,让散列分布更加均匀。
    如果length是奇数:length-1后一定是偶数,偶数二进制位最后一位一定是0,0和任何数进行与运算,结果一定是0,则只能利用数组中偶数位的索引位置进行存储,这样就会导致发生大量的哈希冲突,白白浪费了一半的空间。

3.4.9 HashMap的初始化容量的设置

  1. 当哈希表中的元素越来越多的时候,散列碰撞的几率也就越来越高(因为数组的长度是固定的),从而导致单链表过长,降低了哈希表的性能,此时我们就需要对哈希表进行扩容操作。
  2. 那么HashMap什么时候进行扩容呢?当执行put()操作的时候,如果HashMap中存储元素的个数超过数组长度*loadFactor的结果(loadFactor指的是负载因子,loadFactor的默认值一般为0.75),那么就需要执行数组扩容操作。
  3. 所谓的扩容操作,就是把数组的空间大小扩大一倍,然后遍历哈希表中元素,把这些元素重新均匀分散到扩容后的哈希表中。例如,默认情况下,数组大小为16,那么当HashMap中元素个数超过160.75=12的时候,就需要执行扩容操作,把数组的大小扩展为216=32,然后重新计算每个元素在数组中的位置,这是一个非常消耗性能的操作。
  4. 为了避免扩容带来的性能损坏,建议使用哈希表之前,先预测哈希表需要存储元素的个数,提前为哈希表中的数组设置合适的存储空间大小,避免去执行扩容的操作,进一步提升哈希表的性能。例如:我们需要存储1000个元素,按照哈希表的容量设置为2的整数次幂的思想,我们设置哈希表的容量为1024更合适。但是0.75*1024 < 1024,需要执行消耗性能的扩容操作,因此我们设置哈希表的容量为2048更加合适,这样既考虑了&的问题,也避免了扩容的问题。
  5. 思考:当我们创建一个HashMap对象,设置哈希表的容量为15,请问HashMap对象创建成功后,哈希表的实际容量为多少呢???==>实际容量为12,因为即使设置哈希表的容量为15,底层也会变为16,而由于HashMap中存储元素的个数超过数组长度*loadFactor就会扩容,所以在扩容前的实际能存储的容量为16*0.75=12。

3.5 LinkedHashMap

  • LinkedHashMap集合和HashMap集合的用法完全相同,不过LinkedHashMap可以保证插入顺序。
  • LinkedHashMap集合因为可以保证插入顺序,因此效率比HashMap低一些。
  • LinkedHashMap是如何保证插入顺序的?底层采用了双向链表来记录顺序。
  • LinkedHashMap集合底层采用的数据结构是:哈希表 + 双向链表。
  • LinkedHashMap集合的key是:有序不可重复。key部分也需要同时重写hashCode + equals。
  • key的取值可以为null,key如果相同,value也是覆盖。

存储结构图:
在这里插入图片描述

示例代码:

package maptest;import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;public class LinkedHashMapTest01 {public static void main(String[] args) {Map<Integer, String> map = new LinkedHashMap<>();map.put(100, "张三");map.put(5, "李四");map.put(3000, "王五");map.put(20, "赵六");map.put(100, "张三2");map.put(null,null);Set<Map.Entry<Integer, String>> entries = map.entrySet();for (Map.Entry<Integer, String> entry:entries) {System.out.println(entry.getKey() + ":" + entry.getValue());}}
}

运行结果(有序:插入顺序与取出顺序一致):
在这里插入图片描述

3.6 Hashtable

  • Hashtable和HashMap一样,底层也是哈希表。
  • Hashtable的key和value都不能为null。
  • Hashtable是线程安全的,方法上都有synchronized关键字。使用较少,因为保证线程安全有其他方式。
  • Hashtable的初始化容量:11。默认加载因子:0.75
  • Hashtable的扩容策略:2倍。
  • HashMap中能用的方法在Hashtable中也能用,不过Hashtable中有一些传统方法,这些方法不属于集合框架(需要用Hashtable类型的对象调用):
    ①Enumeration keys(); 获取所有key的迭代器
    ②Enumeration elements(); 获取所有value的迭代器
  • 迭代器Enumeration的相关方法
    ①boolean hasMoreElements(); 是否含有元素
    ②E nextElement(); 获取元素
  • Hashtable和HashMap集合的区别:
    ①HashMap集合线程不安全,效率高,key和value允许null。
    ②Hashtable集合线程安全,效率低,key和value不允许null。

示例代码:

package maptest;import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;public class HashtableTest01 {public static void main(String[] args) {Map<Integer,String> map = new Hashtable<>();map.put(100, "张三");map.put(5, "李四");map.put(3000, "王五");map.put(20, "赵六");map.put(100, "张三2");//java.lang.NullPointerException//map.put(123,null);//java.lang.NullPointerException//map.put(null,"钱七");//java.lang.NullPointerException//map.put(null,null);//继承的迭代方法Set<Map.Entry<Integer, String>> entries = map.entrySet();for (Map.Entry<Integer, String> entry:entries) {System.out.println(entry.getKey() + ":" + entry.getValue());}System.out.println("=============================");Hashtable<Integer,String> hashtable = (Hashtable<Integer, String>) map;//Hashtable独有的迭代方式Enumeration<Integer> keys = hashtable.keys(); //获取所有keywhile (keys.hasMoreElements()){Integer key = keys.nextElement();System.out.println(key);}Enumeration<String> values = hashtable.elements(); //获取所有valuewhile (values.hasMoreElements()){String value = values.nextElement();System.out.println(value);}}
}

运行结果:
在这里插入图片描述

3.7 Properties

  • Properties被称为属性类。通常和xxx.properties属性文件一起使用。
  • Properties的父类是Hashtable。因此Properties也是线程安全的。
  • Properties不支持泛型,key和value只能是String类型,且均不能为null。
  • Properties相关方法:
    ①Object setProperty(String key, String value); 和put方法一样。
    ②String getProperty(String key); 通过key获取value
    ③Set<String> propertyNames(); 获取所有的key

示例代码:

package maptest;import java.util.Enumeration;
import java.util.Properties;public class PropertiesTest01 {public static void main(String[] args) {//创建Properties对象Properties properties = new Properties();//向对象中存储key和valueproperties.setProperty("jdbc.driver", "com.mysql.jdbc.Driver");properties.setProperty("jdbc.user", "root");properties.setProperty("jdbc.password","123456");properties.setProperty("jdbc.url", "jdbc:mysql://localhost:3306/sunny");//通过key获取valueString driver = properties.getProperty("jdbc.driver");String user = properties.getProperty("jdbc.user");String password = properties.getProperty("jdbc.password");String url = properties.getProperty("jdbc.url");System.out.println(driver);System.out.println(user);System.out.println(password);System.out.println(url);System.out.println();//获取所有的keyEnumeration<?> names = properties.propertyNames();while (names.hasMoreElements()){String name = (String) names.nextElement();System.out.println(name + "=" + properties.getProperty(name));}}
}

运行结果:
在这里插入图片描述

3.8 一些树的介绍

3.8.1 二叉树

叉树(BinaryTree)由一个结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树组成。下图中展现了五种不同基本形态的二叉树。
在这里插入图片描述
(a) 为空树。
(b) 为仅有一个结点的二叉树。
© 是仅有左子树而右子树为空的二叉树。
(d) 是仅有右子树而左子树为空的二叉树。
(e) 是左、右子树均非空的二叉树。

3.8.2 排序二叉树

排序二叉树采用左小右大原则存储,按照中序遍历方式,自动就是排好序的。

  • 中序遍历:左根右
  • 前序遍历:根左右
  • 后序遍历:左右根

比如:我们要将数据【14, 12, 23, 4, 16, 13, 8, 3】存储到排序二叉树中,结果如下图所示:
在这里插入图片描述

排序二叉树的问题: 排序二叉树本身实现了排序功能,可以快速检索。但如果插入的节点集本身就是有序的,要么是由小到大排列,要么是由大到小排列,那么最后得到的排序二叉树将变成普通的链表,其检索效率就会很差。

同上数据,先进行排序变成:【3, 4, 8, 12, 13, 14, 16, 23】,然后存储到排序二叉树中,显然就变成了链表,如下图所示:
在这里插入图片描述

3.8.3 平衡二叉树(AVL)

为了避免出现上述一边倒的存储,提出了“平衡二叉树”。

  • 在平衡二叉树中任何结点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。 增加和删除结点可能需要通过一次或多次树旋转来重新平衡这个树。
  • 结点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子-2或2的节点被认为是不平衡的,并需要重新平衡这个树。
  • 比如,我们存储排好序的数据【3, 4, 8, 12, 13, 14, 16, 23】,增加结点如果出现不平衡,则通过节点的左旋或右旋,重新平衡树结构,最终平衡二叉树如下图所示(另参见:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html)
    在这里插入图片描述

3.8.4 红黑二叉树

红黑二叉树(简称:红黑树),它首先是一棵二叉树,同时也是一棵自平衡的排序二叉树。
红黑树在原有的排序二叉树增加了如下几个要求:

  1. 每个结点要么红色,要么黑色。
  2. 根结点永远是黑色。
  3. 所有的叶子结点都是空结点(即null),并且是黑色的。
  4. 每个红色结点的两个子结点都是黑色 (从每个叶子结点到根结点的路径上不会有两个连续的红色结点) 。
  5. 从任一结点到其子树中每个叶子结点的路径都包含相同数量的黑色结点。
  6. 每次新结点在插入时,颜色是红色的。插入后,会根据红黑树的约束条件进行:树的旋转和颜色的调整。

自平衡的含义:以上这些约束强化了红黑树的关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。这样就让树大致上是平衡的。

红黑树是一个更高效的检索二叉树,JDK 提供的集合类 TreeMap、TreeSet 本身就是一个红黑树的实现。红黑树的基本操作:插入、删除、左旋、右旋、着色。每插入或者删除一个节点,可能会导致树不在符合红黑树的特征,需要进行修复,进行 “左旋、右旋、着色” 操作,使树继续保持红黑树的特性。
在这里插入图片描述

3.9 TreeMap

①TreeMap底层就是红黑树。
②TreeMap和HashMap用法一样,只不过需要key排序的时候,就可以使用TreeMap。
③TreeMap的key可排序不可重复,且不能是null,不过value可以为null。
④TreeMap的key也需要重写hashCode和equals方法。

key为内置类型时可排序示例代码:

package maptest;import java.util.Map;
import java.util.Set;
import java.util.TreeMap;public class TreeMapTest01 {public static void main(String[] args) {//创建HashMap集合Map<Integer, String> map = new TreeMap<>();//存入map.put(100, "张三");map.put(5, "李四");map.put(13, "王五");map.put(20, "赵六");map.put(100, "张三2");//java.lang.NullPointerException
//        map.put(null, "zhangzhang");map.put(1, "钱七");//遍历Set<Map.Entry<Integer, String>> entries = map.entrySet();for (Map.Entry<Integer, String> entry: entries) {System.out.println(entry.getKey() + "=" + entry.getValue());}}
}

运行结果(由于Integer实现了Comparable接口,实现了compareTo方法,是可排序的,所以这里按key部分排序输出了):
在这里插入图片描述

3.9.1 TreeMap集合的key为自定义类型

当TreeMap集合的key为自定义类型,要使TreeMap集合的key可排序,有两种方式:

  • 第一种方式:key实现Comparable接口,并且提供compareTo方法,在该方法中添加了比较规则。(比较规则不变的话建议这种。)
  • 第二种方式:创建TreeMap集合时,传一个比较器,比较器实现Comparator接口,在compare方法中添加比较规则。

自定义类型User(当前为不可排序):

package maptest;import java.util.Objects;public class User {private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return age == user.age &&Objects.equals(name, user.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}

测试代码:

package maptest;import java.util.Map;
import java.util.TreeMap;public class TreeMapTest02 {public static void main(String[] args) {//key中存储自定义类型UserMap<User, String> map = new TreeMap<>();//创建User对象User user1 = new User("ccc", 18);User user2 = new User("ddd", 33);User user3 = new User("bbb", 15);User user4 = new User("aaa", 18);//向map中存入元素map.put(user1,"1111");map.put(user2,"2222");map.put(user3,"3333");map.put(user4,"4444");System.out.println(map);}
}

运行结果(由于TreeMap在根据key排序时试图将User转换为Comparable接口,但这里没有实现这个接口,导致出现类型转换异常):
在这里插入图片描述

第一种方式示例代码:

User实现Comparable接口:

package maptest;import java.util.Objects;public class User implements Comparable<User>{private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return age == user.age &&Objects.equals(name, user.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic int compareTo(User o) {//年龄一致的话,比较名字if(this.age == o.age){return this.name.compareTo(o.name);}return this.age - o.age; //年龄不一致,直接按年龄比较}
}

测试代码如上不变,运行结果:
在这里插入图片描述

第二种方式示例代码:
User还是原来的User,不实现Comparable接口:

package maptest;import java.util.Objects;public class User {private String name;private int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;User user = (User) o;return age == user.age &&Objects.equals(name, user.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}

再定义一个比较器(这里使用匿名内部类):

package maptest;import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;public class TreeMapTest03 {public static void main(String[] args) {//创建TreeMap时,给构造方法传递一个比较器,这里直接使用匿名内部类的方式传入比较器Map<User, String> map = new TreeMap<>(new Comparator<User>() {@Overridepublic int compare(User o1, User o2) {return o1.getAge() - o2.getAge(); //按年龄比较}});//创建User对象User user1 = new User("zhangsan", 18);User user2 = new User("lisi", 23);User user3 = new User("wangwu", 25);User user4 = new User("zhaoliu", 16);//向map中存入元素map.put(user1,"1");map.put(user2,"2");map.put(user3,"3");map.put(user4,"4");System.out.println(map);}
}

运行结果:
在这里插入图片描述

4.Set

Set也是Collection集合家族的,只不过Set底层都是Map,所以上面先把Map学习了。

  • Set接口继承Collection,没有任何新增任何方法。
  • Set接口常用实现类包括:HashSet、LinkedHashSet、TreeSet。

4.1 HashSet

  • HashSet底层就是HashMap,往HashSet集合中存储元素,实际上是放到了HashMap集合的key部分。因此放在HashSet集合中的元素,要同时重写hashCode+equals。
  • HashSet底层当然也是哈希表。
  • HashSet集合存储元素特点:无序不可重复。无序指的是存进去的顺序和取出的顺序不一定一样。

1)示例代码(无序不可重复):

package settest;import java.util.HashSet;
import java.util.Set;public class HashSetTest01 {public static void main(String[] args) {Set<Integer> set = new HashSet<>();//无序不可重复set.add(200);set.add(200);set.add(200);set.add(10);set.add(2);set.add(3000);System.out.println(set);Set<String> set2 = new HashSet<>();//无序不可重复set2.add("aaa");set2.add("aaa");set2.add("bbb");set2.add("ccc");set2.add("ddd");set2.add("eee");System.out.println(set2);}
}

运行结果:
在这里插入图片描述

2)示例代码(自定义类型需要重写hashCode和equals方法):

自定义Vip类(未重写hashCode和equals方法):

package settest;public class Vip {private int id;private int age;private String name;public Vip() {}public Vip(int id, int age, String name) {this.id = id;this.age = age;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Vip{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';}
}

测试代码:

package settest;import java.util.HashSet;
import java.util.Set;public class HashSetTest02 {public static void main(String[] args) {//创建HashSetSet<Vip> vips = new HashSet<>();//创建Vip对象Vip vip1 = new Vip(111,20,"zhangsan");Vip vip2 = new Vip(111,20,"zhangsan");Vip vip3 = new Vip(111,20,"zhangsan");//存入vips.add(vip1);vips.add(vip2);vips.add(vip3);System.out.println(vips);}
}

运行结果(三个key内容相同的都存进去了):
在这里插入图片描述
自定义Vip类(增加重写hashCode和equals方法):

package settest;import java.util.Objects;public class Vip {private int id;private int age;private String name;public Vip() {}public Vip(int id, int age, String name) {this.id = id;this.age = age;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Vip{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';}@Overridepublic boolean equals(Object o) { if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Vip vip = (Vip) o;return id == vip.id; //id一致则为返回为true}@Overridepublic int hashCode() {return Objects.hash(id);}
}

测试代码运行结果(只存进去一个):
在这里插入图片描述

4.2 LinkedHashSet

  • LinkedHashSet底层就是LinkedHashMap。所以底层是“哈希表+双向链表”。
  • LinkedHashSet集合存储元素特点:有序不可重复。有序指的是存进去的顺序和取出的顺序一样。放进去的元素也需要重写hashCode+equals。

示例代码:

package settest;import java.util.LinkedHashSet;
import java.util.Set;public class LinkedHashSetTest {public static void main(String[] args) {Set<Integer> set = new LinkedHashSet<>();//有序不可重复set.add(200);set.add(200);set.add(200);set.add(10);set.add(2);set.add(3000);System.out.println(set);Set<String> set2 = new LinkedHashSet<>();//有序不可重复set2.add("aaa");set2.add("aaa");set2.add("bbb");set2.add("ccc");set2.add("ddd");set2.add("eee");System.out.println(set2);}
}

运行结果(存入顺序和取出顺序一致):
在这里插入图片描述
key为自定义类型重写hashCode和equals方法就不写了…

4.3 TreeSet

  • TreeSet底层就是TreeMap。所以底层也是红黑树。
  • TreeSet集合存储元素特点:有序不可重复。有序表示可排序。放在TreeSet集合中元素要想排序,要么存储的元素实现Comparable接口,要么在构造TreeSet集合的时候传一个Comparator比较器。不可重复:放进去的元素也需要重写hashCode+equals。
  • TreeSet中不能存放null。
    示例代码(Vip类如上):
package settest;import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;public class TreeSetTest01 {public static void main(String[] args) {//创建TreeSet对象Set<Integer> set = new TreeSet<>();//存入set.add(100);set.add(23);set.add(5);set.add(18);set.add(1);System.out.println(set);//创建TreeSet对象Set<String> set2 = new TreeSet<>();//存入set2.add("abb");set2.add("bbb");set2.add("abc");set2.add("bba");set2.add("acb");System.out.println(set2);//创建存放自定义类型的TreeSet对象,并传入比较器Set<Vip> vips = new TreeSet<>(new Comparator<Vip>() {@Overridepublic int compare(Vip o1, Vip o2) {return o1.getId() - o2.getId(); //按id排序}});//创建Vip对象Vip vip1 = new Vip(113,20,"zhangsan");Vip vip2 = new Vip(112,20,"lisi");Vip vip3 = new Vip(111,20,"wangwu");//存入vips.add(vip1);vips.add(vip2);vips.add(vip3);System.out.println(vips);}
}

运行结果(有序输出):
在这里插入图片描述

4.4 关于HashSet的面试题

有一个Student类:

package settest;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;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic 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);}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}

阅读以下代码,回答其中的问题:

package settest;import java.util.HashSet;public class HashSetTest03 {public static void main(String[] args) {HashSet<Student> set = new HashSet<>();Student stu = new Student("张三", 18);set.add(stu);set.add(new Student("李四", 21));stu.setName("王五");// 问题1:请问是否删除了HashSet集合中的stu对象呢???set.remove(stu);// 问题2:添加以下Student对象是否成功???set.add(new Student("王五", 18));// 问题3:添加以下Student对象是否成功???set.add(new Student("张三", 18));}
}

分析与运行结果:

package settest;import java.util.HashSet;public class HashSetTest03 {public static void main(String[] args) {//这种题目需要头脑清醒,一步一步分析//创建HashSet对象HashSet<Student> set = new HashSet<>();//创建Student对象stuStudent stu = new Student("张三", 18);//存入set.add(stu);//存入新的对象set.add(new Student("李四", 21));//修改stu对象的name,由于set中存放的是stu的地址,所以set中对应的stu的name也修改了,但是set中stu对应节点还是采用的之前 张三和年龄18  生成的哈希值stu.setName("王五");System.out.println(set);// 问题1:请问是否删除了HashSet集合中的stu对象呢???//不能删除,remove寻找时,使用的是修改后的 王五 和年龄18 的哈希值 进行寻找,而stu节点还是采用的之前 张三和年龄18  生成的哈希值,并不一致,所以并不能找到set.remove(stu);System.out.println(set);// 问题2:添加以下Student对象是否成功???//添加成功,同上,存入时是按 王五 和年龄18 生成的哈希值,不会与当前哈希值产生冲突,可以直接存入set.add(new Student("王五", 18));System.out.println(set);// 问题3:添加以下Student对象是否成功???//添加成功,存入时会与stu节点产生哈希冲突,但是由于name不一样,equals返回为false,所以判定并不是相同节点,可以尾插法存入set.add(new Student("张三", 18));System.out.println(set);}
}

在这里插入图片描述

5.Collections工具类

测试几个针对List集合的常用方法:
①排序方法:sort
②混排,打乱顺序:shuffle
③反转:reverse
④替换所有元素:fill

package collectionstest;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/*** 测试集合工具类java.util.Collections*/
public class CollectionsTest {public static void main(String[] args) {//1.sort方法,专门针对List集合提供的一个sort方法List<Integer> list = new ArrayList<>();list.add(100);list.add(12);list.add(8);list.add(7);list.add(10);System.out.println(list);Collections.sort(list);System.out.println(list);System.out.println("============================");//如果List集合中的元素是自定义类型的,若需要使用sort排序,// 则该自定义类型需要实现Comparable接口,提供比较规则,// 或者排序时传入一个Comparator比较器List<Person> personList = new ArrayList<>();Person p1 = new Person(21);Person p2 = new Person(11);Person p3 = new Person(18);Person p4 = new Person(20);personList.add(p1);personList.add(p2);personList.add(p3);personList.add(p4);System.out.println(personList);Collections.sort(personList);System.out.println(personList);System.out.println("============================");//2.shuffle方法打乱顺序System.out.println(list);Collections.shuffle(list);System.out.println(list);System.out.println("============================");//3.reverse方法 反转List集合中的元素System.out.println(personList);Collections.reverse(personList);System.out.println(personList);System.out.println("============================");//4.fill替换所有元素System.out.println(list);Collections.fill(list,null);System.out.println(list);}
}class Person implements Comparable<Person>{private int age;public Person() {}public Person(int age) {this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"age=" + age +'}';}@Overridepublic int compareTo(Person o) {return this.age - o.age;}
}

运行结果:在这里插入图片描述

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

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

相关文章

2024年在Vim中开发vue2+java

neovim 0.5刚出来的时代&#xff0c;那时刚有lua插件我很狂热。每天沉迷于打造自己的IDE之中。写过一堆相关的博客&#xff0c;也录过一些视频教程。后来发现neovim的接口和插件更新的很快&#xff0c;导致配置文件要不定期的修改&#xff0c;才能保证新版本的插件的适配。我也…

理解思维链Chain of Thought(CoT)

Chain of Thought&#xff08;CoT&#xff09;&#xff0c;即“思维链”&#xff0c;是人工智能领域中的一个概念&#xff0c;特别是在自然语言处理和推理任务中。它指的是一种推理过程&#xff0c;其中模型在生成最终答案之前&#xff0c;先逐步推导出一系列的中间步骤或子目标…

部署Zabbix5.0

一.部署zabbix客户端 端口号10050 zabbix 5.0 版本采用 golang 语言开发的新版本客户端 agent2 。 zabbix 服务端 zabbix_server 默认使用 10051 端口&#xff0c;客户端 zabbix_agent2 默认使用 10050 端口。 1.1.关闭防火墙和selinux安全模块 systemctl disable --now fir…

json diff patch

文件和图片的比对靠字符串 目录 流程 安装 效果 使用 自适应 数组&#xff1a;最长公共子序列(LCS) 数组中的对象&#xff0c;给定id&#xff0c;类似dom tree的比较 流程 安装 npm install jsondiffpatch import * as jsondiffpatch from jsondiffpatch; const jsond…

Vue3(六):Vue3其他API、Vue3新组件Teleport、Vue2和3区别

一、其他API 1.shallowRef 与 shallowReactive &#xff08;1&#xff09;shallowRef 1. 作用&#xff1a;创建一个响应式数据&#xff0c;但只对顶层属性进行响应式处理。 2.用法&#xff1a; let myVar shallowRef(initialValue); 3. 特点&#xff1a;只跟踪引用值的变化&…

IntelliJ IDEA配置类注释模板和方法注释模板

配置类注释模板和方法注释模板 IDEA模板预定义变量类注释模方法注释模板方法参数优化 IDEA模板 在IDEA中&#xff0c;自带的注释模板可能不满足自身需求或者不满意&#xff0c;此时可以通过配置IDEA模板来解决。 预定义变量 内置模板是可编辑的&#xff0c;除了静态文本、代码和…

react中关于类式组件和函数组件对props、state、ref的使用

文章中有很多蓝色字体为扩展链接&#xff0c;可以补充查看。 常用命令使用规则 组件编写方式: 1.函数式 function MyButton() { //直接return 标签体return (<>……</>); }2.类 class MyButton extends React.Component { //在render方法中&#xff0c;return…

GPT-3.5和GPT-Plus的区别

GPT-3.5和GPT-Plus都是OpenAI开发的大型语言模型,但它们之间有一些区别: GPT-3.5就是大家熟知的ChatGPT GPT-Plus 是Open AI 的更强的AI模型GPT-4版本。两者区别是&#xff1a; 模型规模:GPT-Plus是GPT-3的一个更大版本,参数量更多。而GPT-3.5是GPT-3的一个优化版本,在参数量…

基于Copula函数的风光功率联合场景生成_任意修改生成的场景数目(附带Matlab代码)

基于Copula函数的风光功率联合场景生成 削减为6个场景 部分展示削减为5个场景 部分展示 风光等可再生能源出力的不确定性和相关性给系统的设计带来了极大的复杂性&#xff0c;若忽略这些因素&#xff0c;势必会在系统规划阶段引入次优决策风险。因此&#xff0c;在确定系统最佳…

TR-DPO:革新大模型对齐技术

这篇论文中提出的TR-DPO&#xff08;Trust Region Direct Preference Optimization&#xff09;方法的架构或流程设计主要侧重于改进语言模型对齐过程中的稳定性和有效性。 在传统的对齐方法中&#xff0c;模型在学习的时候需要尽可能地靠近一个预先设定的参考模型。这就像是给…

c语言,单链表的实现----------有全代码!!!!

1.单链表的定义和结构 单链表是一种链式的数据结构&#xff0c;它用一组不连续的储存单元存反线性表中的数据元素。链表中的数据是以节点的形式来表示的&#xff0c;节点和节点之间相互连接 一般来说节点有两部分组成 1.数据域 &#xff1a;数据域用来存储各种类型的数据&…

Windows系统下查看C语言文件反汇编

一、配置编译器环境变量 1.下载mingw64 MinGW 的全称是&#xff1a;Minimalist GNU on Windows &#xff0c;MinGW 就是 GCC 的 Windows 版本 。 MinGW-w64 与 MinGW 的区别在于 MinGW 只能编译生成32位可执行程序&#xff0c;而 MinGW-w64 则可以编译生成 64位 或 32位 可执行…

day11 | 栈与队列 part-3 (Go) | 239 滑动窗口最大值、347 前 K 个高频元素 (好难)

今日任务 239 滑动窗口最大值 (题目:. - 力扣&#xff08;LeetCode&#xff09; )347 前 K 个高频元素 (题目: . - 力扣&#xff08;LeetCode&#xff09; )栈与队列总结 239 滑动窗口最大值 题目:. - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums&#xf…

处理json文件,并将数据汇总至Excel表格

从scores.jason文件中读取学生信息,输出学生的学号&#xff0c;姓名&#xff0c;各科成绩&#xff0c;平均分, 各科标准差 scores.jason {"学院": "计算机学院","班级": "2022级1班","成绩": [{"学号": 1001,&q…

使用avx2 指令集加速向量算法运算

使用cpu-z 查看cpu指令集 2 向量加&#xff0c;乘法&#xff0c;除法 我们使用向量加&#xff0c;为什么函数是0 到 8 的计算&#xff0c;因为avx2 寄存器为256位&#xff0c;同时设置启动增强指令集 #include <immintrin.h> // 引入包含AVX2指令集的头文件void vecto…

C++类和对象:赋值重载,const成员

文章目录 1.赋值运算符重载1.1运算符重载1.2 赋值运算符重载1.3 前置和后置重载 2.日期类的实现3. const成员函数4 取地址及const取地址操作符重载 上文介绍了前三个默认成员函数&#xff0c;本文会介绍剩下三个&#xff0c; 赋值重载会重点展开。 1.赋值运算符重载 1.1运算符…

代码随想录 Day17 字符串 | LC344 反转字符串 LC541 反转字符串II 卡码网54替换数字

一、反转字符串 题目&#xff1a; 力扣344&#xff1a;反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题…

微服务相关

1. 微服务主要七个模块 中央管理平台&#xff1a;生产者、消费者注册&#xff0c;服务发现&#xff0c;服务治理&#xff0c;调用关系生产者消费者权限管理流量管理自定义传输协议序列化反序列化 2. 中央管理平台 生产者A在中央管理平台注册后&#xff0c;中央管理平台会给他…

不说成为Linux高级工程师,但成为合格的软件开发人员还是够了,一文深入浅出的精炼总结Linux核心知识点,掌握Linux的使用与高阶技巧

不说成为Linux高级工程师&#xff0c;但成为合格的软件开发人员还是够了&#xff0c;一文深入浅出的精炼总结Linux核心知识点&#xff0c;掌握Linux的使用与高阶技巧。 Linux 的学习对于一个程序员的重要性是不言而喻的。前端开发相比后端开发&#xff0c;接触 Linux 机会相对…

图像基础—图像分类

图像通常分为二值图像、灰度图像和彩色图像 图 1-3 二值图像、灰度图像和彩色图像 &#xff08;1&#xff09;二值图像 二值图像又称为黑白图像&#xff0c;图像中任何一个点非黑即白&#xff0c;要么为白色&#xff08;像素 为 255&#xff09;&#xff0c;要么为黑色&#x…