【Java集合篇】HashMap的remove方法是如何实现的?

在这里插入图片描述

HashMap的remove方法是如何实现的

  • ✔️典型解析
  • ✔️拓展知识仓
    • ✔️HashMap的remove方法的注意事项
    • ✔️HashMap的remove方法的参数类型
    • ✔️ 删除键和值的参数类型有什么区别
      • ✔️删除键值对的场景是什么
    • ✔️HashMap remove方法是阻塞队列的吗
    • ✔️HashMap remove方法是线程安全的吗
      • ✔️什么是分段锁技术(上面提到在这里做简单的概括,随后细说)
    • ✔️HashMap remove方法的优缺点
    • ✔️HashMap的remove方法怎么实现同步
  • ✔️源码解读


✔️典型解析


下面是JDK 1.8中HashMap的remove方法的简要实现过程:

1 . 首先,remove方法会计算键的哈希值,并通过哈希值计算出在数组中的索引位置。


2 . 如果该位置上的元素为空,说明没有找到对应的键值对,直接返回null。


3 . 如果该位置上的元素不为空,检查是否与当前键相等,如果相等,那么将该键值对删除,并返回该键值对的值。


4 . 如果该位置上的元素不为空,但也与当前键不相等,那么就需要在链表或红黑树中继续查找。


5 . 遍历链表或者红黑树,查找与当前键相等的键值对,找到则将该键值对删除,并返回该键值对的值,否则返回null。


HashMap 是 Java 中的一种数据结构,它实现了 Map 接口。HashMap 的 remove 方法主要用于从 map 中删除指定的键值对。以下是 HashMap 的 remove 方法的基本实现:

public V remove(Object key) {Node<K,V> e;if ((e = removeNode(hash(key), key)) == null)return null;V oldVal = e.value;modCount++;size--;return oldVal;
}

这段代码做了以下几件事情:

  1. removeNode(hash(key), key):这是 HashMap 的核心部分,它尝试删除给定键的节点。如果这个键存在,那么对应的节点就会被删除。这个方法需要两个参数:一个是键的哈希值,另一个是键本身。哈希值用于在数组中定位节点,而键用于在链表中定位要删除的节点。
  2. e = null:如果给定的键不存在于 map 中,那么 removeNode 方法将返回 null。在这种情况下,remove 方法也将返回 null
  3. V oldVal = e.value:如果给定的键存在于 map 中,那么对应的节点将被删除,并且节点的值将被存储在 oldVal 变量中。这个值稍后将被返回。
  4. modCount++:这是对修改计数器的递增。这个计数器用于检测 map 的结构是否在迭代过程中被修改。如果 map 在迭代过程中被修改,那么迭代器将抛出 ConcurrentModificationException。
  5. size--:这是对 map 大小的递减。因为一个键值对已经被删除,所以 map 的大小必须减少。
  6. return oldVal:最后,方法返回被删除的键值对的值。

HashMap的remove方法是通过删除特定键对应的节点来实现的。HashMap中的每个元素实际上是一个Node对象,它包含键值对(key-value pair)。在remove方法中,首先通过哈希值和键来定位要删除的节点。具体来说,首先计算出键的哈希值,然后使用这个哈希值在HashMap的数组中找到对应的索引位置。然后,在这个索引位置的链表中,通过键来找到要删除的节点。


一旦找到了要删除的节点,就将它从链表中移除,并返回该节点的值。同时,还需要更新哈希表的两个关键属性:哈希表的长度和哈希表的容量。


此外,HashMap的remove方法还需要处理并发修改异常的问题。如果多个线程同时修改HashMap,就可能出现ConcurrentModificationException异常。为了避免这个问题,当一个元素被删除后,需要将modCount(修改计数器)加一,以便在迭代过程中检测到HashMap的结构是否被修改。


这就是HashMap的remove方法的基本实现过程。需要注意的是,这个过程可能会根据不同的Java版本和不同的HashMap实现有所不同。


✔️拓展知识仓


✔️HashMap的remove方法的注意事项


使用HashMap的remove方法时,需要注意以下几点:

  1. 线程安全:在多线程环境下,如果多个线程同时调用remove方法删除哈希表中的元素,不会出现线程安全问题,因为ConcurrentHashMap是线程安全的。

  1. 返回值:remove方法返回被删除元素对应的value。需要注意的是,如果不存在该key,则返回值为null。因此,在程序中使用remove方法时,需要对返回值进行严格的判断。

  1. 性能考虑:由于HashMap是非同步的,因此在高并发情况下,如果不进行外部同步,可能会遇到数据不一致的问题。

  1. null值:HashMap允许使用null作为键和值。调用remove方法时,要特别注意是否可能删除一个null值。

  1. 并发修改异常:如果在迭代过程中对HashMap进行修改(包括使用remove方法),则可能会抛出ConcurrentModificationException。如果需要遍历并可能修改HashMap,建议使用Iterator进行操作。

  1. 初始容量和负载因子:在创建HashMap时,需要设置初始容量和负载因子。初始容量决定了哈希表的大小,而负载因子决定了何时重新哈希。如果预计存储的键值对数量很大,可能需要设置较大的初始容量和负载因子以优化性能。

  1. 内存回收:删除操作不会立即回收内存,需要等到垃圾回收机制运行时才会释放内存。

  1. 其他操作的影响:除了remove方法外,其他操作如put、get等也可能影响HashMap的性能和行为。在设计程序时,需要考虑各种操作的组合和顺序。

使用HashMap的remove方法时,需要注意多线程环境下的线程安全、返回值的处理、性能优化、null值的处理、并发修改异常的处理、初始容量和负载因子的设置、内存回收以及与其他操作的协同工作等问题。


✔️HashMap的remove方法的参数类型


HashMap的remove方法有两种参数类型:

  1. remove(Object key):以键为参数的remove方法,用于删除指定键的键值对。如果该键存在于map中,对应的键值对将被删除并返回该键的值。如果该键不存在于map中,返回null。

  2. remove(Object key, Object value):以键和值为参数的remove方法,用于删除指定键和值的键值对。只有当键和值都匹配时,对应的键值对才会被删除。如果成功删除了键值对,返回true;否则返回false。

这两种remove方法都是在调用内部的removeNode方法进行节点删除,只是传入的参数不同。

看 一个Demo,简单的项目案例:



import java.util.HashMap;  
import java.util.Scanner;  /**
*   如何使用HashMap的remove方法来管理一个虚构的电子商务网站的库存
*/  
public class InventorySystem {  public static void main(String[] args) {  // 创建一个HashMap来存储商品库存  HashMap<String, Integer> inventory = new HashMap<>();  // 添加初始商品库存  inventory.put("Laptop", 10);  inventory.put("Smartphone", 20);  inventory.put("Tablet", 5);  // 创建一个Scanner对象,从控制台读取输入  Scanner scanner = new Scanner(System.in);  // 循环处理用户输入,直到用户选择退出  while (true) {  // 打印菜单选项给用户  System.out.println("请选择操作:");  System.out.println("1. 添加商品");  System.out.println("2. 删除商品");  System.out.println("3. 查看库存");  System.out.println("4. 退出系统");  // 读取用户输入的选择  int choice = scanner.nextInt();  switch (choice) {  case 1: // 添加商品  String productName = scanner.next();  int quantity = scanner.nextInt();  inventory.put(productName, quantity);  System.out.println("商品已添加到库存。");  break;  case 2: // 删除商品  String productToRemove = scanner.next();  if (inventory.containsKey(productToRemove)) {  int removedQuantity = inventory.remove(productToRemove);  System.out.println("删除了 " + removedQuantity + " 个 " + productToRemove + "。");  } else {  System.out.println("库存中没有找到 " + productToRemove + "。");  }  break;  case 3: // 查看库存  System.out.println("当前库存:");  for (String product : inventory.keySet()) {  System.out.println(product + " - " + inventory.get(product) + "个");  }  break;  case 4: // 退出系统  System.out.println("感谢使用库存系统!");  System.exit(0); // 退出程序  break;  default: // 如果用户输入无效选项,提示用户重新输入  System.out.println("无效的选择,请重新输入。");  }  }  }  
}

案例中,创建一个虚构的电子商务网站的库存管理系统。我们使用HashMap来存储商品和它们的数量。用户可以通过控制台菜单选择不同的操作,如添加商品、删除商品、查看库存或退出系统。根据用户的选择,程序将使用HashMap的put方法添加商品、使用remove方法删除商品、打印库存情况或者结束程序。通过这种方式,我们演示了如何使用HashMap的remove方法来管理一个复杂的项目案例。


✔️ 删除键和值的参数类型有什么区别


HashMap的remove方法有两种参数类型,它们的主要区别在于删除键值对的条件不同。

  1. remove(Object key):这个参数类型的remove方法用于删除指定键的键值对。它只检查键是否存在于map中,如果存在,则删除对应的键值对并返回该键的值;如果不存在,则返回null。这个方法只关心键是否存在,而不关心值的内容。

  1. remove(Object key, Object value):这个参数类型的remove方法用于删除指定键和值的键值对。它同时检查键和值是否匹配,只有当键和值都匹配时,对应的键值对才会被删除。这个方法不仅关心键是否存在,还关心值的内容是否匹配。

这两种参数类型的remove方法在使用上有不同的场景和需求。如果你只需要删除指定键的键值对,无论值的内容如何,可以使用remove(Object key)方法。如果你需要同时考虑键和值的内容是否匹配,可以使用remove(Object key, Object value)方法。


✔️删除键值对的场景是什么


删除键值对的场景通常出现在需要更新或删除存储在数据结构中的数据时。在HashMap中,键值对表示一种映射关系,即通过键可以快速找到对应的值。因此,在需要更新或删除某个键对应的值时,就可以使用删除键值对的方法。

如,在数据库操作中,可以根据主键(键)快速定位到相应的记录(值),然后进行删除或更新操作。在缓存系统中,可以使用键值对存储缓存数据,当某个键不再需要时,就可以通过删除键值对来释放缓存空间。

在其他数据结构中,如数组、链表等,删除键值对的场景可能不太常见,因为这些数据结构通常只关注元素的添加、删除和查找等操作,而不是关注键值对的关系。

总之,删除键值对的场景通常出现在需要维护键和值之间映射关系的数据结构中,如HashMap等。在这些场景中,删除键值对可以快速定位并更新或删除相应的数据,提高数据操作的效率和准确性。


✔️HashMap remove方法是阻塞队列的吗


HashMapremove 方法不是阻塞队列的。HashMap是一种基于数组和链表的数据结构,用于存储键值对映射关系。它提供了快速的插入、删除和查找操作。

阻塞队列是一种特殊类型的队列,当队列为空时,获取元素的线程将会阻塞,直到队列中有元素可用。同样,当队列已满时,尝试添加元素的线程也将阻塞,直到队列有空余空间。

HashMap本身并不具备阻塞队列的特性,它的remove方法只是用于删除指定键的键值对。如果需要使用阻塞队列,可以考虑使用Java提供的BlockingQueue接口及其实现类,如ArrayBlockingQueueLinkedBlockingQueue等。

总之,HashMap的remove方法不是阻塞队列的,它只是用于删除键值对。如果需要使用阻塞队列,建议使用Java提供的BlockingQueue接口及其实现类。


首先,回顾一下HashMapremove方法。这是一个非同步的方法,它从哈希表中删除指定的键值对。如果哈希表中包含指定的键值对,则返回该键值对的值;否则,返回null。这个方法不是线程安全的,因此在多线程环境中使用时需要额外的同步措施。

下面看一个Demo,说明HashMapremove方法不是阻塞的,以及如何使用BlockingQueue实现阻塞删除操作:

import java.util.HashMap;public class HashMapExample {public static void main(String[] args) {// 创建一个HashMapHashMap<String, String> map = new HashMap<>();// 添加元素到HashMap中map.put("key1", "value1");map.put("key2", "value2");map.put("key3", "value3");// 删除一个元素String removedValue = map.remove("key2");if (removedValue != null) {System.out.println("成功删除了键为\"key2\"的元素,值为\"" + removedValue + "\"。");} else {System.out.println("未找到键为\"key2\"的元素。");}// 打印剩余的元素System.out.println("剩余的元素:");for (String key : map.keySet()) {System.out.println(key + " - " + map.get(key));}}
}

现在,看看如何使用BlockingQueue实现阻塞删除操作。BlockingQueue是一个线程安全的队列接口,它提供了阻塞的插入和删除操作。我们可以使用BlockingQueuetake方法来阻塞地删除并返回队列的头元素。如果队列为空,该方法将阻塞等待直到有元素可用。

接下来这个Demo,演示如何使用BlockingQueue实现阻塞删除操作:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;public class BlockingQueueExample {public static void main(String[] args) throws InterruptedException {// 创建一个BlockingQueue实例BlockingQueue<String> queue = new LinkedBlockingQueue<>();// 添加元素到队列中queue.put("item1");queue.put("item2");queue.put("item3");// 阻塞地删除并返回队列的头元素String removedItem = queue.take(); // 阻塞等待直到有元素可用System.out.println("成功删除了元素\"" + removedItem + "\"。");}
}

通过这个Demo,可以看到BlockingQueuetake方法会阻塞等待直到有元素可用,然后删除并返回队列的头元素。这与HashMapremove方法不同,后者是非阻塞的,并且不会等待元素可用。如果你需要在多线程环境中安全地删除元素,并希望操作是阻塞的,你应该使用BlockingQueue而不是HashMap


✔️HashMap remove方法是线程安全的吗


HashMap的remove方法不是线程安全的。在多线程环境下,如果没有适当的同步措施,使用HashMap的remove方法可能会导致数据不一致或其他并发问题。


在Java中,HashMap是一个非同步的类,这意味着它的操作(包括remove方法)默认情况下是线程不安全的。如果你需要在多线程环境中安全地删除元素,应该考虑使用线程安全的集合类,如ConcurrentHashMap


上一篇谈HashMap的get方法时有谈,可参考!!!


ConcurrentHashMap是线程安全的,它提供了高性能的并发访问能力,适用于高并发环境。ConcurrentHashMap使用分段锁技术来实现线程安全,允许多个线程同时访问不同的段(Segment),从而提高了并发性能。


因此,一个线程安全的集合类来安全地删除元素,应该使用ConcurrentHashMap而不是HashMap


✔️什么是分段锁技术(上面提到在这里做简单的概括,随后细说)


分段锁技术是一种用于实现并发数据结构的锁策略,它将数据结构分成多个段,每个段都有自己的锁。具体来说,分段锁技术将数据分成一段一段的存储,然后给每一段数据配一把锁。当一个线程需要访问某个段的数据时,只需要获取该段对应的锁,而不会影响其他段的操作。这样可以减小锁的粒度,从而提高并发性能。


在具体实现上,分段锁技术将数据结构划分为多个段,每个段对应一个锁。当一个线程需要访问某个段的数据时,会先通过某种机制(如散列函数)确定该段数据的存储位置,然后获取该段对应的锁。这样,不同的线程可以同时访问不同的段数据,从而实现更高的并发性。


分段锁技术的优点包括:

  1. 并发性高:由于每个段都有自己的锁,不同的线程可以同时访问不同的段数据,提高了并发性能。
  2. 细粒度控制:通过将数据结构划分为多个段,可以更灵活地控制对数据的访问权限,提高了系统的可扩展性和灵活性。
  3. 更少的争用:如果数据分布比较均匀,每个锁锁住的数据区域不会太大,减少了锁的争用。

分段锁技术的缺点包括:

  1. 空间开销较大:需要为每个锁分配一定的内存空间,如果锁过多,会占用较多的资源。
  2. 管理复杂:需要管理和维护每个锁的状态和状态变化,增加了管理复杂性。

分段锁技术是一种高效的并发控制方法,适用于高并发、大数据量的场景。它通过将数据结构划分为多个段,减小了锁的粒度,提高了并发性能。但是需要注意管理复杂性和空间开销等问题。


✔️HashMap remove方法的优缺点


HashMap的remove方法是一种常用的数据结构操作,其优缺点如下:

优点:

  1. 删除指定键值对:HashMap的remove方法允许用户删除指定的键值对,从而有效地管理数据。
  2. 高效性:在大多数情况下,HashMap的remove方法具有较高的效率,因为它使用哈希表来存储数据。删除操作可以在常数时间内完成。
  3. 线程不安全:HashMap的remove方法不是线程安全的。如果多个线程同时对HashMap进行操作,可能会导致数据不一致的问题。

缺点:

  1. 线程不安全:如上所述,HashMap的remove方法不是线程安全的。如果需要在多线程环境中安全地使用remove方法,需要使用其他同步措施,如使用ConcurrentHashMap。
  2. 数据不一致:由于HashMap的remove方法不是线程安全的,可能会导致数据不一致的问题。例如,在删除一个键值对时,如果其他线程同时修改了该键值对,可能会导致数据不一致。
  3. 依赖哈希函数:HashMap的remove方法依赖于哈希函数来存储和访问数据。如果键的哈希值相同但键不相同,它们将被存储在同一个桶中,导致冲突。这可能会影响删除操作的效率。

HashMap的remove方法具有简单、高效等优点,但也存在线程不安全、数据不一致等缺点。如果需要在多线程环境中安全地使用remove方法,建议使用ConcurrentHashMap等线程安全的集合类。


✔️HashMap的remove方法怎么实现同步


在Java中,HashMap的remove方法不是线程安全的。如果你需要在多线程环境中安全地使用remove方法,可以使用其他线程安全的集合类,如ConcurrentHashMap

然而,如果你仍然想使用HashMap并实现同步,可以使用synchronized关键字对 remove 方法进行同步。这将确保一次只有一个线程可以执行remove方法。以下是一个示例:

public class SynchronizedHashMap {private final HashMap<String, String> map = new HashMap<>();public synchronized void remove(String key) {map.remove(key);}
}

示例中,创建了一个名为 SynchronizedHashMap 的类,它包含一个HashMap成员变量map。然后,我们为remove方法添加了synchronized关键字,以确保在多线程环境中该方法的安全性。

注意:使用synchronized关键字对整个remove方法进行同步可能会导致性能下降,因为一次只有一个线程可以执行该方法。如果需要更高的并发性能,建议使用ConcurrentHashMap等线程安全的集合类。


✔️源码解读


public V remove(Object key) {Node<k,V> e;return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
}

还是像前面的老习惯,重点还是来看下 removeNode 方法:


/**
* Implements Map.remove and related methods
* @param hash        hash 值
* @param key         key 值
* @param value       value 值
* @param matchValue  是否需要值匹配 false 表示不需要
* @param movable     不用管
*  @return the node , or null if none
*/
final Node<k, V> removelode(int hash, Object key, Object value, boolean matchValue, boolean movable) {//当前HashMap 中的散列表的引用Node<KV>[] tab;//p: 表示当前的Node元素Node<k,V> p;// n: table 的长度// index: 桶的下标位置int n, index;//(tab = table) != nul & (n = tab.length) >0 条件成立,说明table不为空 (table 为空就没必要执行了)// p = tabrindex = (n - 1) & hash]) != null 将定位到的捅位的元素赋值给 p 并判断定位到的元素不为空if ((tab = table) != null && (n = tab,length) > 0 && (p = tabindex = (n - 1) & hashl) != null) {//进到 if 里面来了,说明已经定位到元素了//node:保存查找到的结果//e: 表示当前元素的下一个元素Node<k,V> node = nul1, e;K k;V v;//该条件如果成立,说明当前的元素就是要找的结果(这是最简单的情况,这个是很好理解的)if (p.hash == hash && ((k = p.key) == key  (key != null && key.equals(k))))  {node = p;}//到这一步,如果 (e = p.next) != null 说明该捅位找到的元素可能是链表或者是树,需要继续判断else if ((e = p.next) != null) {//树,不考虑if (p instanceof TreeNode) {node = ((TreeNode<KV>) p).getTreeNode(hash, key);}//处理链表的情说else {do {//如果条件成立,说明已经匹配到了元素,直接将查找到的元素赋值给 node,并跳出循环(总体还是很好理解的)if (e.hash == hash && ((k = e.key) == key (key != null && key.eguals(k)))) {node = e;break:}//将正在遍历的当前的临时元素 e 赋值给 Pp = e ;} while ((e = e.next) != null);}}// node != null 说明匹配到了元素//matchValue为false ,所以!matchValue = true,后面的条件直接不用看了if (node != null && (!matchValue  (v = node.value) == value  (value != null && value.equals(v)))) {//树,不考虑if (node instanceof TreeNode) {((TreeNode<k,V>) node) .removeTreeNode(this,tab,movable);}//这种情况是上面的最简单的情况else if (node == p) {//直接将当前节点的下一个节点放在当前的桶位置《注意不是下一个桶位置,是该桶位置的下一个节点)tab[index] = node.next;} else {//说明定位到的元素不是该桶位置的头元素了,那直接进行一个简单的链表的操作即可p.next = node.next ;}//移除和添加都属于结构的修改,需要同步自增 modCount 的值++modCount;//table 中的元素个数减 1--size;//啥也没做,不用管afterNodeRemoval(node);//返回被移除的节点元素return node;}}//没有匹配到返回null 即可return null:
}

我想对你们说的话,都写在注释里面咯,宝子们一定要好好看哦!

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

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

相关文章

如何使用web文件管理器Net2FTP搭建个人网盘

文章目录 1.前言2. Net2FTP网站搭建2.1. Net2FTP下载和安装2.2. Net2FTP网页测试 3. cpolar内网穿透3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 文件传输可以说是互联网最主要的应用之一&#xff0c;特别是智能设备的大面积使用&#xff0c;无论是个人…

Linux 常用指令汇总

Linux 常用指令汇总 文章目录 Linux 常用指令汇总[toc]前言一、文件目录指令pwd 指令ls 指令cd 指令mkdir 指令rmdir 指令tree 指令cp 指令rm 指令mv 指令cat 指令more 指令less 指令head 指令tail 指令echo 指令> 指令>> 指令 二、时间日期指令date 指令cal 指令 三、…

Mars3D与mars3d-cesium版本间兼容造成3dtiles和gltf数据处理相关记录

说明&#xff1a; 1.在引入Mars3D SDK后正常可以在F12打印的信息中可以看到Mars3D和Cesium版本信息。 2.在项目的package.json文件中同样可以看到安装后的版本号。 Mars3D对Cesium版本对应关系&#xff1a; 1.正常情况下mars3d依赖的cesium均是最新版本&#xff0c;并且对cesi…

53K star! 平替TeamViewer,试试这个开源神器

还记得以前工作上遇到困难&#xff0c;会申请开发大神远程帮忙&#xff0c;那时候用都是TeamViewer&#xff0c;但是随着TeamViewer的收费&#xff0c;这些都已成为过往。 今天我们推荐的开源项目就是让你可以轻松平替TeamViewer&#xff0c;一款远程桌面神器&#xff0c;本项…

Antd使用table同时使用scroll和fixed定位,滑动导致左右高度不统一

今天使用组件里Table时候&#xff0c;遇到一个业务场景就是在有scroll控制滚动条的同时&#xff0c;固定部分列&#xff0c;就出现的如题的bug 问题&#xff1a;无法对齐 代码&#xff1a; <TabledataSource{data}scroll{{ y: calc(100vh - 275px), x: 1200px }}columns{t…

智能合约介绍

莫道儒冠误此生&#xff0c;从来诗书不负人 目录 一、什么是区块链智能合约? 二、智能合约的发展背景 三、智能合约的优势 四、智能合约的劣势 五、一些关于智能合约的应用 总结 一、什么是区块链智能合约? 智能合约&#xff0c;是一段写在区块链上的代码&#xff0c;一…

0x33 同余

0x33 同余 定义 若整数 a a a和整数 b b b除以正整数 m m m的余数相等&#xff0c;则称 a , b a,b a,b模 m m m同余&#xff0c;记为 a ≡ b ( m o d m ) a\equiv b(mod\ m) a≡b(mod m)。 同余类与剩余类 对于 ∀ a ∈ [ 0 , m − 1 ] \forall a\in[0,m-1] ∀a∈[0,m−1]&…

Simpy简介:python仿真模拟库-03/5

一、说明 在过去的两篇文章中&#xff0c;我们了解了 simpy 的基础知识、声明变量和处理表达式。值得注意的例子包括评估导数和积分。现在&#xff0c;让我们继续使用函数。 二、SymPy — 函数类 SymPy 包包含 sympy.core.function 模块中的 Function 类。该类作为各种数学函数…

构建labelstudio镜像的时候,报错node:18,如何解决

解决方案&#xff1a; vi Dockerfile # syntaxdocker/dockerfile:1.3 FROM --platformlinux/amd64 node:18.16-bullseye-slim AS frontend-builder18改成 18.16-bullseye-slim

Spring框架-Spring JDBC

文章目录 Spring JDBC实例1&#xff0c;导入依赖2&#xff0c;配置数据源3&#xff0c;数据库4&#xff0c;实体类5&#xff0c;dao层5&#xff0c;配置Dao Bean6&#xff0c;业务逻辑层7&#xff0c;配置Service Bean8&#xff0c;测试 JdbcTemplate常用方法1,导入依赖2&#…

【漏洞复现】锐捷EG易网关login.php命令注入漏洞

Nx01 产品简介 锐捷EG易网关是一款综合网关&#xff0c;由锐捷网络完全自主研发。它集成了先进的软硬件体系架构&#xff0c;配备了DPI深入分析引擎、行为分析/管理引擎&#xff0c;可以在保证网络出口高效转发的条件下&#xff0c;提供专业的流控功能、出色的URL过滤以及本地化…

【2024系统架构设计】 系统架构设计师第二版-通信系统架构设计理论与实践

目录 一 通信系统网络架构 二 网络构建的关键技术 三 网络构建和设计方法 四 案例分析 注:本节内容可作为知识储备,做一个基本的了解即可。

50天精通Golang(第12天)

channel通道 通道可以被认为是Goroutines通信的管道。类似于管道中的水从一端到另一端的流动&#xff0c;数据可以从一端发送到另一端&#xff0c;通过通道接收。 在前面讲Go语言的并发时候&#xff0c;我们就说过&#xff0c;当多个Goroutine想实现共享数据的时候&#xff0…

Linux-命名管道

文章目录 前言一、命名管道接口函数介绍二、使用步骤 前言 上章内容&#xff0c;我们介绍与使用了管道。上章内容所讲的&#xff0c;是通过pipe接口函数让操作系统给我们申请匿名管道进行进程间通信。 并且这种进程间通信一般只适用于父子进程之间&#xff0c;那么对于两个没有…

原来这些小众知识库软件这么好用,挖到宝了

在企业管理中&#xff0c;知识库的作用越来越被重视。它不仅可以提高工作流程的效率&#xff0c;还可以最大限度地利用企业中的知识资源。然而&#xff0c;在众多的知识库工具中选择一款合适的并非易事。不用担心&#xff0c;今天我要为大家揭晓一些小众却非常好用的知识库软件…

【华为OD机试真题2023CD卷 JAVAJS】中文分词模拟器

华为OD2023(C&D卷)机试题库全覆盖,刷题指南点这里 中文分词模拟器 知识点图字符串 时间限制:5s 空间限制:256MB 限定语言:不限 题目描述: 给定一个连续不包含空格字符串,该字符串仅包含英文小写字母及英文文标点符号(逗号、分号、句号),同时给定词库,对该字符串进…

python函数装饰器基础

1 python函数装饰器基础 python2.5开始支持函数装饰器。装饰器语法&#xff1a;自动调用装饰器函数&#xff0c;返回可调用对象重新绑定到主体函数名。 1.1 基础用法 函数装饰器用法&#xff0c;装饰器函数&#xff0c;下一行接def 主体函数&#xff1a; decorator def fm(…

微信小程序实战-02翻页时钟-2

微信小程序实战系列 《微信小程序实战-01翻页时钟-1》 文章目录 微信小程序实战系列前言计时功能实现clock.wxmlclock.wxssclock.js 运行效果总结 前言 接着《微信小程序实战-01翻页时钟-1》&#xff0c;继续完成“6个页面的静态渲染和计时”功能。 计时功能实现 clock.wxm…

python类装饰器基础

1 python类装饰器基础 类装饰器是管理类的一种方式&#xff0c;不是包装单个函数或方法&#xff0c;而是用装饰器的额外逻辑&#xff0c;来包装实例的构建和调用&#xff0c;从而实现对主体类的扩展。 主体类被装饰后&#xff0c;通过主体类创建实例对象时&#xff0c;自动调…

Linux前后端项目部署

目录 1.jdk&tomcat安装 配置并且测试jdk安装 修改tomcat 配置文件 登入tomcat 发布 安装mysql 导入sql数据 发布项目war包 redis安装 nginx安装 配置nginx域名映射 部署前端项目 centos 7的服务安装 安装jdk 安装tomcat 安装Mysql 安装redis 安装nginx 前后…