最新资讯

  • Java集合笔记总结

Java集合笔记总结

2026-02-01 06:26:47 栏目:最新资讯 3 阅读

Java集合

引言

1 说说有哪些常见的集合框架?

Java中集合类主要两大类:Collection和Map,Collection只能装一种,Map装两种类型

1.Collection 接口:最基本的集合框架表示方式,提供了添加、删除、清空等基本操作,它主要有三个子接口:

  • List:一个有序的集合,可以包含重复的元素。实现类包括 ArrayList、LinkedList 等。
  • Set:一个不包含重复元素的集合。实现类包括 HashSet、LinkedHashSet、TreeSet 等。
  • Queue:一个用于保持元素队列的集合。实现类包括 PriorityQueue、ArrayDeque 等。

2.Map 接口:表示键值对的集合,一个键映射到一个值。键不能重复,每个键只能对应一个值。Map 接口的实现类包括 HashMap、LinkedHashMap、TreeMap 等。

(1)集合框架有哪几个常用工具类?Arrays和Collections

两个工具类:Collections和Arrays

  • Collections:提供了一些对集合进行排序、二分查找、同步的静态方法。

  • Arrays:提供了一些对数组进行排序、打印、和 List 进行转换的静态方法。

(2)简单介绍一下队列

先进先出的数据结构,Queue接口,实现类双端队列、优先队列

java中的队列主要有两个接口实现:Queue接口和并发包下的BlockingQueue接口

优先队列PriorityQueue是一个无界队列,它的元素是按照自然顺序排序或者 Comparator 比较器进行排序。

java默认通过小顶堆进行实现的,数字越小越靠前

PriorityQueue自定义排序,主要看比较器的实现

// 核心构造函数
PriorityQueue(Comparator<? super E> comparator)

1.大顶堆:

默认是 1, 2, 3… 出队。如果你想让 9, 8, 7… 出队

// 写法 1:使用 Lambda 表达式 (推荐,最简洁)
// (o1, o2) -> o2 - o1 表示降序
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);

// 写法 2:使用 Collections.reverseOrder()
PriorityQueue<Integer> maxHeap2 = new PriorityQueue<>(Collections.reverseOrder());

2.自定义对象排序

// 方式 1:经典 Lambda 写法
PriorityQueue<Student> pq = new PriorityQueue<>((s1, s2) -> {
    // 降序:后减前 (s2 - s1)
    // 升序:前减后 (s1 - s2)
    return s2.score - s1.score; 
});

// 方式 2:使用 Comparator.comparing (更现代,防溢出)
// reversed() 表示反转,即从大到小
PriorityQueue<Student> pq = new PriorityQueue<>(
    Comparator.comparingInt(Student::getScore).reversed()
);

3.多条件排序:

PriorityQueue<Student> pq = new PriorityQueue<>((s1, s2) -> {
    if (s1.score != s2.score) {
        return s2.score - s1.score; // 分数降序
    } else {
        return s1.name.compareTo(s2.name); // 名字升序 (String默认是升序)
    }
});

双端队列

双端队列 ArrayDeque 是一个基于数组的,可以在两端插入和删除元素的队列。

LinkedList实现了Deque也可以当最哦双端队列来使用

(3)用过哪些集合类,它们的优劣?

常用的集合类有ArrayList,HashSet,LinkedList、LinkedHashMap

  1. ArrayList 可以看作是一个动态数组,可以在需要时动态扩容数组的容量,只不过需要复制元素到新的数组。**优点是访问速度快,可以通过索引直接查找到元素。**缺点是插入和删除元素可能需要移动或者复制元素。

  2. LinkedList 是一个双向链表,适合频繁的插入和删除操作。优点是插入和删除元素的时候只需要改变节点的前后指针,缺点是访问元素时需要遍历链表。

  3. HashMap 是一个基于哈希表的键值对集合。优点是可以根据键的哈希值快速查找到值,但有可能会发生哈希冲突,并且不保留键值对的插入顺序。

  4. LinkedHashMap 在 HashMap 的基础上增加了一个双向链表来保持键值对的插入顺序

(4)队列和栈的区别了解吗?

队列先进先出,栈先进后出

  • 队列是一种先进先出(FIFO, First-In-First-Out)的数据结构,第一个加入队列的元素会成为第一个被移除的元素,适用于需要按顺序处理任务的场景,比如消息队列、任务调度等。
  • 栈是一种后进先出(LIFO, Last-In-First-Out)的数据结构,最后一个加入栈的元素会成为第一个被移除的元素,适用于需要回溯的场景,比如函数调用栈、浏览器历史记录等。

(5)哪些是线程安全的容器?

容器类型非线程安全 (普通)线程安全 (推荐 JUC)线程安全 (不推荐/古董)特点/适用场景
MapHashMapConcurrentHashMapHashtable锁粒度细,高并发首选
ListArrayListCopyOnWriteArrayListVector读多写少,写时复制
SetHashSetCopyOnWriteArraySet-底层基于 CopyOnWriteArrayList
QueueLinkedListArrayBlockingQueue/LinkedBlockingQueue-生产者消费者模型

[!NOTE]

第一代:上古时代的“独占锁” (不推荐)

这代容器的特点是:笨重。 不管你是读还是写,它直接在一个方法上加 synchronized,相当于把整个容器锁死。一个线程在操作,其他线程全部排队。

  1. Vector:线程安全的 ArrayList。
  2. Hashtable:线程安全的 HashMap。
    • 缺点:性能太差。多线程环境下,竞争激烈时简直就是灾难。

第二代:包装类 (过渡方案)

通过 Collections 工具类把普通的容器“包装”成线程安全的。

  1. Collections.synchronizedList(new ArrayList<>())
  2. Collections.synchronizedMap(new HashMap<>())
    • 特点:本质上还是第一代的思路,加了一层粗粒度的锁(Mutex),性能提升有限。

第三代:JUC 并发容器 (面试核心,重点掌握)

这才是现代 Java 高并发的基石,主要采用了 CAS (无锁算法)分段锁写时复制 等高级机制。

1. Map 家族:ConcurrentHashMap (最重要)

它是 HashMap 的线程安全版,但性能吊打 Hashtable。

  • 原理 (简单版)
    • Hashtable 是一把大锁锁住整个哈希表(锁全家)。
    • ConcurrentHashMap (JDK8) 是**“锁细化”**。它只锁住哈希桶的头节点(Node)。也就是说,如果两个线程操作的是不同的 Key(比如一个操作 Key=“A”,一个操作 Key=“Z”),它们互不影响,可以并行操作!
  • 适用场景:绝大多数需要线程安全 Map 的场景。
  1. List 家族:CopyOnWriteArrayList

它是 ArrayList 的线程安全版。

  • 原理 (读写分离)
    • 读 (Read):完全不加锁,随便读,速度极快。
    • 写 (Write):当要修改数据时,它不直接改原数组,而是把原数组复制 (Copy) 一份新的,在新的上面修改,改完后再把引用指过去。
  • 缺点:写操作很贵(要复制内存),且有数据延迟(读的时候可能还没写完)。
  • 适用场景读多写少(比如黑白名单、配置列表)。
  1. Queue 家族:阻塞队列 (BlockingQueue)

这是生产者-消费者模型的神器。它们不仅线程安全,还有一个特性:队列满了会自动阻塞生产者,队列空了会自动阻塞消费者(不用你自己写 wait/notify)。

  • ArrayBlockingQueue:基于数组,有界(必须指定大小)。
  • LinkedBlockingQueue:基于链表,可选有界(默认是 Integer.MAX_VALUE,容易 OOM,要注意)。

(6)Collection 继承了哪些接口?

Iterable接口,必须实现iterator方法,这意味着所有实现 Collection 接口的类都必须实现 iterator() 方法,之后就可以使用增强型 for 循环遍历集合中的元素了。

List

2 ArrayList 和 LinkedList 有什么区别?

ArrayList 是基于动态数组实现的,LinkedList 是基于双向链表实现的。

(1)ArrayList 和 LinkedList 的用途有什么不同?

多数情况下,ArrayList 更利于查找,LinkedList 更利于增删。按照道理来说

1.由于 ArrayList 是基于数组实现的,所以 get(int index) 可以直接通过数组下标获取,时间复杂度是 O(1);LinkedList 是基于链表实现的,get(int index) 需要遍历链表,时间复杂度是 O(n)。

当然,get(E element) 这种查找,两种集合都需要遍历通过 equals 比较获取元素,所以时间复杂度都是 O(n)。

2.ArrayList 如果增删的是数组的尾部,时间复杂度是 O(1);如果 add 的时候涉及到扩容,时间复杂度会上升到 O(n)。

但如果插入的是中间的位置,就需要把插入位置后的元素向前或者向后移动,甚至还有可能触发扩容,效率就会低很多,变成 O(n)。

(2)ArrayList 和 LinkedList 是否支持随机访问?

ArrayList 是基于数组的,也实现了 RandomAccess 接口,所以它支持随机访问,可以通过下标直接获取元素。

LinkedList 是基于链表的,所以它没法根据下标直接获取元素,不支持随机访问。

(3)ArrayList 和 LinkedList 内存占用有何不同?

  1. ArrayList基于数组,是一块连续的内存空间,所以它的内存占用是比较紧凑的;但如果涉及到扩容,就会重新分配内存,空间是原来的 1.5 倍。

  2. LinkedList 是基于链表的,每个节点都有一个指向下一个节点和上一个节点的引用,于是每个节点占用的内存空间比 ArrayList 稍微大一点。

(4)ArrayList 和 LinkedList 的使用场景有什么不同?

按道理来说查询多修改少用ArrayList,修改多查询少的情况下用LinkedList

。但是实际情况下百分之90的情况都会使用ArrayList,这是因为计算机组成原理CPU缓存的命中率的原因。连续内存更容易命中,LinkedList比较分散不容易命中,并且数组搬用移动使用System.arraycopy()的原生方法,非常快

3 ArrayList 的扩容机制了解吗?

如果元素+1会超出容量就会进行1.5倍扩容。然后再把原数组的值拷贝到新数组中。

4 ArrayList 怎么序列化的知道吗?

arrayList的序列化,是使用writeObject和readObject方法的,并且eleement数组是使用transient去修饰的,因为数组的容量一般是用不完大于实际元素的容量,这样就可以自定义的序列化有用的数据。

(1)为什么 ArrayList 不直接序列化元素数组呢?

出于效率的考虑,数组可能长度 100,但实际只用了 50,剩下的 50 没用到,也就不需要序列化。

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    // 将当前 ArrayList 的结构进行序列化
    int expectedModCount = modCount;
    s.defaultWriteObject(); // 序列化非 transient 字段
    // 序列化数组的大小
    s.writeInt(size);
    // 序列化每个元素
    for (int i = 0; i < size; i++) {
        s.writeObject(elementData[i]);
    }
    // 检查是否在序列化期间发生了并发修改
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

5 快速失败fail-fast了解吗?

(1)什么是安全失败(fail—safe)呢?

6 有哪几种实现 ArrayList 线程安全的方法?

线程安全就是要实现能在多线程下使用,保持原子性+可见性+有序性

两种方法让ArrayList实现线程安全:使用synchronizedList和CopyOnWriteArrayList’

一般情况下读多写少使用CopyOnwriteArrayList,写多了要么干脆就使用ConcurrentLinkedList

  • synchronizedList

可以使用 Collections.synchronizedList() 方法,它可以返回一个线程安全的 List。

SynchronizedList list = Collections.synchronizedList(new ArrayList());
  • SynchronizedList通过内部加锁实现

CopyOnWriteArrayList它是线程安全的 ArrayList,遵循写时复制的原则,每当对列表进行修改时,都会创建一个新副本,这个新副本会替换旧的列表,而对旧列表的所有读取操作仍然在原有的列表上进行。感觉很像mvcc的实现

通俗的讲,CopyOnWrite 就是当我们往一个容器添加元素的时候,不直接往容器中添加,而是先复制出一个新的容器,然后在新的容器里添加元素,添加完之后,再将原容器的引用指向新的容器。多个线程在读的时候,不需要加锁,因为当前容器不会添加任何元素。这样就实现了线程安全。

(1)ArrayList 和 Vector 的区别?

Vector 属于 JDK 1.0 时期的遗留类,不推荐使用,仍然保留着是因为 Java 希望向后兼容。

ArrayList 是在 JDK 1.2 时引入的,用于替代 Vector 作为主要的非同步动态数组实现。因为 Vector 所有的方法都使用了 synchronized 关键字进行同步,所以单线程环境下效率较低。

7 CopyOnWriteArrayList 了解多少?

写时复制,就是写的时候克隆新副本,在新副本上执行写操作,然后指向新的容器。

CopyOnWriteArrayList 就是线程安全版本的 ArrayList。

CopyOnWriteArrayList 采用了一种读写分离的并发策略。CopyOnWriteArrayList 容器允许并发读,读操作是无锁的。至于写操作,比如说向容器中添加一个元素,首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。

Map

Map 中最重要的就是 HashMap 了,面试基本被问出包浆了,一定要好好准备。

8 能说一下 HashMap 的底层数据结构吗?

JDK 8 中 HashMap 的数据结构是数组+链表+红黑树

键值对,键怎么得到

当多个键经过哈希处理后得到相同的索引时,需要通过链表来解决哈希冲突——将具有相同索引的键值对通过链表存储起来。

不过,链表过长时,查询效率会比较低,于是当链表的长度超过 8 时(且数组的长度大于 64),链表就会转换为红黑树红黑树的查询效率是 O(logn),比链表的 O(n) 要快。

hash() 方法的目标是尽量减少哈希冲突,保证元素能够均匀地分布在数组的每个位置上。

// 扰动函数,进一步处理哈希值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

如果键的哈希值已经在数组中存在,其对应的值将被新值覆盖。

HashMap 的初始容量是 16,随着元素的不断添加,HashMap 就需要进行扩容,阈值是capacity * loadFactor,capacity 为容量,loadFactor 为负载因子,默认为 0.75。

扩容后的数组大小是原来的 2 倍,然后把原来的元素重新计算哈希值,放到新的数组中。

(1)负载因子干什么用的?

负载因子(load factor)是一个介于 0 和 1 之间的数值,用于衡量哈希表的填充程度。它表示哈希表中已存储的元素数量与哈希表容量之间的比例。

  • 负载因子过高(接近 1)会导致哈希冲突增加,影响查找、插入和删除操作的效率。
  • 负载因子过低(接近 0)会浪费内存,因为哈希表中有大量未使用的空间。

默认的负载因子是 0.75,这个值在时间和空间效率之间提供了一个良好的平衡。

9 你对红黑树了解多少?

红黑树本质上是一种自平衡的二叉查找树,它通过以下 5 条铁律来保证平衡:

  1. 颜色属性:每个节点要么是红色,要么是黑色
  2. 根属性根节点必须是黑色
  3. 叶子属性:所有的叶子节点(NIL 节点,即空节点)都是黑色的。
  4. 红色属性(不红红):不能有两个连续的红色节点(即红色节点的子节点必须是黑色)。
  5. 黑色属性(黑高相等):从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。

(1)为什么不用二叉树?

这里应该是说的二叉搜索树BST,基本数据结构,每一个节点最多有两个子节点,比父节点小的子节点都放在左,比父节点的大的都放在右,然后查询的时候在极端情况下会,比如插入节点完全有序(1,2,3,4,5)的情况下就会退化成链表,查询效率变O(n)

(2)为什么不用平衡二叉树?

平衡二叉树AVL要求高,左右子树高度差不会超过1,平衡可以保证极佳的查找效率,但是进行插入、删除的情况就有极高的维护成本。

(3)为什么用红黑树?

红黑树是一种折中方案,链表的查找时间复杂度是 O(n)当链表长度较长时,查找性能会下降。红黑树是一种折中的方案,查找、插入、删除的时间复杂度都是 O(log n)

之所以选择红黑树,是因为它是一种**‘弱平衡’二叉树。 相比于普通 BST,它解决了退化成链表的问题,保证了查询效率。 相比于 AVL 树,它牺牲了微小的查询性能(不再是绝对平衡),换取了更少的旋转操作**,从而大幅提升了插入和删除的效率。 在需要频繁插入删除的实际工程场景(如 HashMap)中,红黑树的综合性能是最好的。

10 红黑树怎么保持平衡的?

通过旋转和染色保持平衡

1.通过左右旋转避免一侧树的节点过深

2.染色,修复红黑该规则,从而保证树的高度不会失衡。

红黑树插入删除规则

参考:https://blog.csdn.net/m0_52383454/article/details/126393163

1. 插入 (主要看叔叔)

  • 红叔叔:父叔变黑,祖变红,递归。
  • 黑叔叔:旋转(LL右旋/RR左旋),父变黑,祖变红。

2. 删除 (主要看兄弟)

  • 删红节点:直接删。
  • 删黑节点(看兄弟):
  • 红兄弟:转成黑兄弟。
  • 黑兄弟 + 黑侄子:兄弟变红,矛盾上移(找爸爸麻烦)。
  • 黑兄弟 + 红侄子:旋转+变色,借个节点过来,彻底解决。

11 HashMap 的 put 流程知道吗?【*】

哈希寻址 → 处理哈希冲突(链表还是红黑树)→ 判断是否需要扩容 → 插入/覆盖节点。(看懂下图差不多)

详细流程:

1.获得哈希扰动哈希值,可以用来减少哈希冲突

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

2.第一次扩容,然后还要计算查找索引位置,该数组位置没有元素就直接放进去

if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

如果当前位置为空,直接将键值对插入该位置;否则判断当前位置的第一个节点是否与新节点的 key 相同,如果相同直接覆盖 value,如果不同,说明发生哈希冲突。

3.发生哈希冲突解决哈希冲突,比较key,key同就完全覆盖的,不同才有冲突解决,链表和红黑树的事

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 如果 table 为空,先进行初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    
    // 计算索引位置,并找到对应的桶
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null); // 如果桶为空,直接插入
    else {
        Node<K,V> e; K k;
        // 检查第一个节点是否匹配
        if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
            e = p; // 覆盖
        // 如果是树节点,放入树中
        else if (p instanceof TreeNode)
            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);
                    // 如果链表长度达到阈值,转换为红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1)
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
                    break; // 覆盖
                p = e;
            }
        }
        if (e != null) { // 如果找到匹配的 key,则覆盖旧值
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount; // 修改计数器
    if (++size > threshold)
        resize(); // 检查是否需要扩容
    afterNodeInsertion(evict);
    return null;
}

每次插入新元素后,检查是否需要扩容,如果当前元素个数大于阈值(capacity * loadFactor),则进行扩容,扩容后的数组大小是原来的 2 倍;并且重新计算每个节点的索引,进行数据重新分布。负载因子默认0.75

(1)只重写元素的 equals 方法没重写 hashCode,put 的时候会发生什么?

内容相等的两个对象,但是hashCode不同,这样两个对象会被放在数组的不同位置,进行get的时候就会出错

12 HashMap 怎么查找元素的呢?

计算索引-》找到桶-》在桶里比较key的内容,然后取得节点获得值

通过哈希值定位索引 → 定位桶 → 检查第一个节点 → 遍历链表或红黑树查找 → 返回结果。

13 HashMap 的 hash 函数是怎么设计的?

是一个扰动函数,然哈希值更加均匀的分布,需要与异或^ 一个(h>>>16)

先拿到 key 的哈希值,是一个 32 位的 int 类型数值,然后再让哈希值的高 16 位和低 16 位进行异或操作,这样能保证哈希分布均匀。

static final int hash(Object key) {
    int h;
    // 如果 key 为 null,返回 0;否则,使用 hashCode 并进行扰动
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

h >>> 16是什么意思:

>>> 是 Java 中的无符号右移运算符

  • 含义:把一个数的二进制位全部向右移动 16 位。
  • 规则:不管这个数是正数还是负数,高位(左边)空出来的地方永远补 0
  • 对比>> (带符号右移) 会在左边补符号位(正数补0,负数补1),而 >>> 无论如何都补 0。

h >>> 16 的作用就是把高 16 位的信息“移动”到低 16 位去

HashMap 计算下标的公式是:index = (n - 1) & hash

  • 绝大多数情况下,HashMap 的数组长度 n 不会很大(比如初始是 16)。
  • n = 16 时,n - 1 是 15 (0000 ... 0000 1111)。
  • 做按位与 (&) 运算时,只有 hash 值的最后 4 位有效,前面的 28 位全被忽略了!

后果: 如果两个 Key 的哈希值,高位不同,但低位相同(比如 A 是 11110000...0101,B 是 00001111...0101),它们算出来的下标完全一样,导致哈希冲突。高位的信息被白白浪费了

高位参与运算

(h = key.hashCode()) ^ (h >>> 16)

h >>> 16 的目的是:让高位的信息参与到低位的运算中。 这样即使数组长度很小(只看低位),高位的变化也能影响到最终的下标,从而让哈希分布得更均匀,减少冲突

14 为什么 hash 函数能减少哈希冲突?

就是让hashCode()的值的高16位参与索引左边的按位与运算

快速回答:哈希表的索引是通过 h & (n-1) 计算的,**n 是底层数组的容量;n-1 和某个哈希值做 & 运算,相当于截取了最低的四位。**如果数组的容量很小,只取 h 的低位很容易导致哈希冲突。n就是很小的用二进制表示就几位,直接按位与很容易哈希冲突

通过异或操作将 h 的高位引入低位,可以增加哈希值的随机性,从而减少哈希冲突

示例:说
以初始长度 16 为例,16-1=15。2 进制表示是0000 0000 0000 0000 0000 0000 0000 1111。只取最后 4 位相等于哈希值的高位都丢弃了。

比如说 1111 1111 1111 1111 1111 1111 1111 1111,取最后 4 位,也就是 1111。

1110 1111 1111 1111 1111 1111 1111 1111,取最后 4 位,也是 1111。

不就发生哈希冲突了吗?

这时候 hash 函数 (h = key.hashCode()) ^ (h >>> 16) 就派上用场了。

哈希值无符号右移 16 位,意味着原哈希值的高 16 位被移到了低 16 位的位置。这样,原始哈希值的高 16 位和低 16 位就可以参与到最终用于索引计算的低位中。

选择 16 位是因为它是 32 位整数的一半,这样处理既考虑了高位的信息,又没有完全忽视低位原本的信息,从而达到了一种微妙的平衡状态。(为什么选16,)

举个例子(数组长度为 16)。

  • 第一个键值对的键:h1 = 0001 0010 0011 0100 0101 0110 0111 1000
  • 第二个键值对的键:h2 = 0001 0010 0011 0101 0101 0110 0111 1000

如果没有 hash 函数,直接取低 4 位,那么 h1 和 h2 的低 4 位都是 1000,也就是说两个键值对都会放在数组的第 8 个位置。

来看一下 hash 函数的处理过程。

对于第一个键h1的计算:

原始: 0001 0010 0011 0100 0101 0110 0111 1000
右移: 0000 0000 0000 0000 0001 0010 0011 0100
异或: ---------------------------------------
结果: 0001 0010 0011 0100 0100 0100 0100 1100

对于第二个键h2的计算:

原始: 0001 0010 0011 0101 0101 0110 0111 1000
右移: 0000 0000 0000 0000 0001 0010 0011 0101
异或: ---------------------------------------
结果: 0001 0010 0011 0101 0100 0100 0100 1101

通过上述计算,我们可以看到h1h2经过h ^ (h >>> 16)操作后得到了不同的结果。

现在,考虑数组长度为 16 时(需要最低 4 位来确定索引):

  • 对于h1的最低 4 位是1100(十进制中为 12)
  • 对于h2的最低 4 位是1101(十进制中为 13)

这样,h1h2就会被分别放在数组的第 12 个位置和第 13 个位置上,从而避免了哈希冲突。

15 为什么 HashMap 的容量是 2 的幂次方?

是为了快速定位元素在底层数组中的下标。

HashMap 是通过 hash & (n-1) 来定位元素下标的,n 为数组的大小,也就是 HashMap 底层数组的容量。

数组长度-1 正好相当于一个“低位掩码”——掩码的低位最好全是 1,这样 & 运算才有意义,否则结果一定是 0。

2 幂次方刚好是偶数,偶数-1 是奇数,奇数的二进制最后一位是 1,也就保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(取决于 hash 的值),这样可以保证哈希值的均匀分布。

换句话说**,& 操作的结果就是将哈希值的高位全部归零,只保留低位值。**

示例:

已知 HashMap 的初始长度为 16,16-1=15,二进制是 00000000 00000000 00001111(高位用 0 来补齐):

	 10100101 11000100 00100101
&	 00000000 00000000 00001111
----------------------------------
	 00000000 00000000 00000101

因为 15 的高位全部是 0,所以 & 运算后的高位结果肯定也是 0,只剩下 4 个低位 0101,也就是十进制的 5。

这样,哈希值为 10100101 11000100 00100101 的键就会放在数组的第 5 个位置上。

(1)对数组长度取模定位数组下标,这块有没有优化策略?

将取模运算转化成位运算

快速回答:HashMap 的策略是将取模运算 hash % table.length 优化为位运算 hash & (length - 1)

因为当数组的长度是 2 的 N 次幂时,hash & (length - 1) = hash % length。这也是数组长度 2 的 N 次幂设置的原因

比如说 9 % 4 = 1,9 的二进制是 1001,4 - 1 = 3,3 的二进制是 0011,9 & 3 = 1001 & 0011 = 0001 = 1。

再比如说 10 % 4 = 2,10 的二进制是 1010,4 - 1 = 3,3 的二进制是 0011,10 & 3 = 1010 & 0011 = 0010 = 2。

当数组的长度不是 2 的 n 次方时,hash % lengthhash & (length - 1) 的结果就不一致了。

比如说 7 % 3 = 1,7 的二进制是 0111,3 - 1 = 2,2 的二进制是 0010,7 & 2 = 0111 & 0010 = 0010 = 2。

从二进制角度来看,hash / length = hash / 2n = hash >> n,即把 hash 右移 n 位,此时得到了 hash / 2n 的商。

(2)说说什么是取模运算?

Java 中,通常使用 % 运算符来表示取余,用 Math.floorMod() 来表示取模。

当操作数都是正数的话,取模运算和取余运算的结果是一样的;只有操作数出现负数的情况下,结果才会不同。

取模运算的商向负无穷靠近;取余运算的商向 0 靠近。这是导致它们两个在处理有负数情况下,结果不同的根本原因。

对于 HashMap 来说,它需要通过 hash % table.length 来确定元素在数组中的位置。

16 如果初始化 HashMap,传一个 17 的容量,它会怎么处理?

17是一个非2的n次方,HashMap 会将容量调整到大于等于 17 的最小的 2 的幂次方,也就是 32,会自己调整

还会获取最近2的倍数去初始化,向上去

这是因为哈希表的大小最好是 2 的 N 次幂,这样可以通过 (n - 1) & hash 高效计算出索引值。

这样的原有有一个阈值,阀值 threshold 会通过⽅法 tableSizeFor() 进⾏计算。

public HashMap(int initialCapacity, float loadFactor) {
 ...
 this.loadFactor = loadFactor;
 this.threshold = tableSizeFor(initialCapacity);
}
// 找到大于或等于 cap 的最小的 2 的幂次方。
static final int tableSizeFor(int cap) {
    int n = cap - 1; // 减 1 的原因:为了防止传入的数本身就是 2 的幂次方时,结果翻倍。
    // 移位逻辑:利用二进制的最高位传播特性,通过不断右移并按位或,把最高位 1 后面的所有位都填成 1,最后加 1 得到目标值。
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

面试怎么答?

如果面试官问这段代码,你只需要回答三个点,他就会觉得你很懂:

  1. 目的:为了将任意整数转换为大于等于它的最小的 2 的幂次方。
  2. 减 1 的原因:为了防止传入的数本身就是 2 的幂次方时,结果翻倍。
  3. 移位逻辑:利用二进制的最高位传播特性,通过不断右移并按位或,把最高位 1 后面的所有位都填成 1,最后加 1 得到目标值。

(1)初始化 HashMap 的时候需要传入容量吗?

如果预先知道就是需要指定的。如果预先知道 Map 将存储大量键值对,提前指定一个足够大的初始容量可以减少因扩容导致的重哈希操作。

resize()会重新分配时间,操作耗时,因为每次扩容时,HashMap 需要将现有的元素插入到新的数组中,这个过程相对耗时,尤其是当 Map 中已有大量数据时。

当然了,过大的初始容量会浪费内存,特别是当实际存储的元素远少于初始容量时。如果不指定初始容量,HashMap 将使用默认的初始容量 16。

17 你还知道哪些哈希函数的构造方法呢?

5种方法:除留取余法、直接定址、取平方、数字分析取数、折叠取和

  • 除留取余法H(key)=key%p(p<=N),关键字除以一个不大于哈希表长度的正整数 p,所得余数为地址,当然 HashMap 里进行了优化改造,效率更高,散列也更均衡。
  • 除此之外,还有这几种常见的哈希函数构造方法:
  • 直接定址法:直接根据key来映射到对应的数组位置,例如 1232 放到下标 1232 的位置。
  • 数字分析法:取key的某些数字(例如十位和百位)作为映射的位置
  • 平方取中法:取key平方的中间几位作为映射的位置
  • key分割成位数相同的几段,然后把它们的叠加和作为映射的位置。

18 解决哈希冲突有哪些方法?

再哈希法、开放地址法、拉链法

1.再哈希法:使用两个哈希算法函数,发生冲突,再使用第二个哈希算法计算,直到没有冲突,核心思想是多重哈希

2.开放地址法:发生冲突就直接放再数组的下一个空位置

3.拉链法:就是现在使用的接拉链,接红黑树的形式

什么是再哈希法?

准备两套哈希算法,当发生哈希冲突的时候,使用另外一种哈希算法,直到找到空槽为止。对哈希算法的设计要求比较高。

什么是开放地址法?

遇到哈希冲突的时候,就去寻找下一个空的槽。有 3 种方法:

  • 线性探测:从冲突的位置开始,依次往后找,直到找到空槽。
  • 二次探测:从冲突的位置 x 开始,第一次增加 12 个位置,第二次增加 22,直到找到空槽。
  • 双重哈希:和再哈希法类似,准备多个哈希函数,发生冲突的时候,使用另外一个哈希函数。

什么是拉链法?

也就是链地址法,当发生哈希冲突的时候,使用链表将冲突的元素串起来。HashMap 采用的正是拉链法。

(1)怎么判断key相等?

使用hashCode()与equals(),流程就是先hashCode判断相等,然后判断内容相等,中间还提前用==判断引用是否相等,相等了就要直接覆盖原值

// 先hashcode再equals
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))

hashCode() :使用keyhashCode()方法计算key的哈希码。

equals() :当两个key的哈希码相同时,HashMap还会调用keyequals()方法进行精确比较。只有当equals()方法返回true时,两个key才被认为是完全相同的。

如果两个key的引用指向了同一个对象,那么它们的hashCode()equals()方法都会返回true,所以在 equals 判断之前可以先使用==运算符判断一次。

19 为什么 HashMap 链表转红黑树的阈值为 8 呢?

直接回答:当数组长度大于等于64,链表长度大于等于8的情况下

概率统计:根据泊松分布,在哈希函数正常的情况下,链表长度达到 8 的概率只有千万分之六。所以 8 基本上是一个‘不可能发生’的事件,选择这个数字是为了让红黑树只在极端情况(哈希攻击或哈希函数极差)下才生效。

空间成本:红黑树节点的大小依然是链表节点的 2 倍。在节点少时,红黑树不仅浪费空间,且查找优势不明显。只有当长度达到 8 时,红黑树的 O ( log ⁡ n ) O(log n) O(logn) 优势才能抵消其空间和维护成本。

防抖动:树化阈值是 8,退化阈值是 6,中间留有缓冲,是为了防止在临界点频繁插入删除导致的数据结构反复转换。”

为什么是8?

和统计学有关。理想情况下,使用随机哈希码,链表里的节点符合泊松分布,出现节点个数的概率是递减的,节点个数为 8 的情况,发生概率仅为 0.00000006

也就是说,在正常情况下,链表长度达到 8 是个小概率事件。

**8 是一个平衡点。**当链表长度小于 8 时,即使是 O(n) 的查找,由于 n 比较小,实际性能还是可以接受的,而且链表的内存开销小。当链表长度达到 8 时,查找性能已经比较差了,这时候转换为红黑树的收益就比较明显了,因为红黑树的查找、插入、删除操作的时间复杂度都是 O(log n)。

20 HashMap扩容发生在什么时候呢?

发生在数组的实际容量超过数组长度*负载因子

(1)默认的负载因子是多少?

0.75

(2)初始容量是多少?

16

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

无论 HashMap 是否扩容,其底层的数组长度都应该是 2 的幂次方,因为这样可以通过位运算快速计算出元素的索引。

(3)为什么是0.75

1.太大容易发生哈希冲突

2.太小浪费空间

这是一个经验值。如果设置得太低,如 0.5,会浪费空间;如果设置得太高,如 0.9,会增加哈希冲突。

0.75 是 JDK 作者经过大量验证后得出的最优解,能够最大限度减少 rehash 的次数。

21 HashMap的扩容机制了解吗?

rehashing()比较耗时的操作,要重新分配冲突元素的位置,详细来说:

扩容时,HashMap 会创建一个新的数组,其容量是原来的两倍。然后遍历旧哈希表中的元素,将其重新分配到新的哈希表中。

如果当前桶是红黑树,那么会调用 split() 方法分裂树节点,以保证树的平衡。

如果当前桶是链表,会通过旧键的哈希值与旧的数组大小取模 (e.hash & oldCap) == 0 来作为判断条件,如果条件为真,元素保留在原索引的位置;否则元素移动到原索引 + 旧数组大小的位置。rehashing

(1)JDK 7 扩容的时候有什么问题?

JDK 7 在扩容的时候使用头插法来重新插入链表节点,这样会导致链表无法保持原有的顺序。

JDK 7 是通过哈希值与数组大小-1 进行与运算确定元素下标的。

static int indexFor(int h, int length) {
    return h & (length-1);
}

我们来假设:

  • 数组 table 的长度为 2
  • 键的哈希值为 3、7、5

取模运算后,键发生了哈希冲突,它们都需要放到 table[1] 的桶上。那么扩容前就是这个样子:

假设负载因子 loadFactor 为 1,也就是当元素的个数大于 table 的长度时进行扩容。

扩容后的数组容量为 4。

  • key 3 取模(3%4)后是 3,放在 table[3] 上。
  • key 7 取模(7%4)后是 3,放在 table[3] 上的链表头部。
  • key 5 取模(5%4)后是 1,放在 table[1] 上。

可以看到,由于 JDK 采用的是头插法,7 跑到 3 的前面了,原来的顺序是 3、7、5,7 在 3 的后面。最好的情况就是,扩容后的 7 还在 3 的后面,保持原来的顺序。

(2)JDK 8 是怎么解决这个问题的?

改用尾插法,并且使用e.hash&oldCap来判断是否要保留原索引的位置。JDK 8 改用了尾插法,并且当 (e.hash & oldCap) == 0 时,元素保留在原索引的位置;否则元素移动到原索引 + 旧数组大小的位置。

// 源码分析
Node<K,V> loHead = null, loTail = null;// 低位,保留
Node<K,V> hiHead = null, hiTail = null;// 高位,移动位置
Node<K,V> next;
do {
    next = e.next;
    if ((e.hash & oldCap) == 0) { // 进行判断,
        if (loTail == null)
            loHead = e;
        else
            loTail.next = e;
        loTail = e;
    }
    else {
        if (hiTail == null)
            hiHead = e;
        else
            hiTail.next = e;
        hiTail = e;
    }
} while ((e = next) != null);
if (loHead != null)
    newTab[j] = loHead;
if (hiHead != null)
    newTab[j + oldCap] = hiHead; // 高位重新移动位置

示例:

原索引 index = (n - 1) & hash,扩容后的新索引就是 index = (2n - 1) & hash

也就是说,如果 (e.hash & oldCap) == 0,元素在新数组中的位置与旧位置相同;否则,元素在新数组中的位置是旧位置 + 旧数组大小。

假设扩容前的数组长度为 16(n-1 也就是二进制的 0000 1111,1X 2 0 {2^0} 20+1X 2 1 {2^1} 21+1X 2 2 {2^2} 22+1X 2 3 {2^3} 23=1+2+4+8=15),key1 为 5(二进制为 0000 0101),key2 为 21(二进制为 0001 0101)。

  • key1 和 n-1 做 & 运算后为 0000 0101,也就是 5;
  • key2 和 n-1 做 & 运算后为 0000 0101,也就是 5。
  • 此时哈希冲突了,用拉链法来解决哈希冲突。

现在,HashMap 进行了扩容,容量为原来的 2 倍,也就是 32(n-1 也就是二进制的 0001 1111,1X 2 0 {2^0} 20+1X 2 1 {2^1} 21+1X 2 2 {2^2} 22+1X 2 3 {2^3} 23+1X 2 4 {2^4} 24=1+2+4+8+16=31)。

  • key1 和 n-1 做 & 运算后为 0000 0101,也就是 5;
  • key2 和 n-1 做 & 运算后为 0001 0101,也就是 21=5+16,就是数组扩容前的位置+原数组的长度。

这样可以避免重新计算所有元素的哈希值,只需检查高位的某一位,就可以快速确定新位置。(直接看高位)

(3)扩容的时候每个节点都要重新计算哈希值吗?

会经过e.hash & oldCap来判断需不需要进行移动,需要移动才会重新计算位置

不需要。HashMap 会通过 (e.hash & oldCap) 来判断节点是否需要移动,0 的话保留原索引;1 才需要移动到新索引(原索引 + oldCap)。

这样就避免了 hashCode 的重新计算,大大提升了扩容的性能。

所以,哪怕有几十万条数据,可能只有一半的数据才需要移动到新位置。另外,位运算的计算速度非常快,因此,尽管扩容操作涉及到遍历整个哈希表并对每个节点进行判断,但这部分操作的计算成本是相对较低的。

[!IMPORTANT]

主要就是高位判断(e.hash & oldCap) == 0

22 JDK 8 对 HashMap 做了哪些优化呢?

主要做了4方面优化:加入红黑树、优化链表插入方式头插变尾插、优化扰动函数、扩容时机

底层数据结构由数组 + 链表改成了数组 + 链表或红黑树的结构。

链表的插入方式由头插法改为了尾插法。头插法在扩容后容易改变原来链表的顺序。

1.7:

1.8:

哈希扰动算法也进行了优化。JDK 7 是通过多次移位和异或运算来实现的

1.7:

JDK 8 让 hash 值的高 16 位和低 16 位进行了异或运算,让高位的信息也能参与到低位的计算中,这样可以极大程度上减少哈希碰撞。

23 你能自己设计实现一个 HashMap 吗?

这个背一下,算法,写数组加链表版本

24 HashMap 是线程安全的吗?【*】

不是,要使用concurrentHashMap,在多线程环境下会出现数据丢失和数据覆盖的问题,在jdk8之前的场景还会出现死锁的情况

1.两个线程同时进行同样两个节点的插入,jdk7种就会把两个节点锁住。JDK7 中的 HashMap 使用的是头插法来处理链表,在多线程环境下扩容会出现环形链表,造成死循环。不过,JDK 8 时通过尾插法修复了这个问题,扩容时会保持链表原来的顺序。

2.数据覆盖,两个线程同时进行put操作,对同一个key进行修改。多线程在进行 put 元素的时候,可能会导致元素丢失。因为计算出来的位置可能会被其他线程覆盖掉,比如说一个县城 put 3 的时候,另外一个线程 put 了 7,就把 3 给弄丢了。

3.数据丢失:put 和 get 并发时,可能导致 get 为 null。线程 1 执行 put 时,因为元素个数超出阈值而扩容,线程 2 此时执行 get,就有可能出现这个问题。因为扩容机制

因为线程 1 执行完 table = newTab 之后,线程 2 中的 table 已经发生了改变,比如说索引 3 的键值对移动到了索引 7 的位置,此时线程 2 去 get 索引 3 的元素就 get 不到了。

25 怎么解决 HashMap 线程不安全的问题呢?【*】

早期,使用HashTable来在多线程的环境下进行使用,HashTable在每一个方法上synchronized关键词进行加锁。

现在,现在建议使用并发包下的concurrentHashMap,使用CAS+synchronzied关键词保证线程安全

还可以通过 Collections.synchronizedMap 方法返回一个线程安全的 Map,内部是通过 synchronized 对象锁来保证线程安全的,比在方法上直接加 synchronized 关键字更轻量级。

CAS:乐观锁,全程compare and switch,比较并交换,是一种无锁的原子操作。

在 CAS 中,有这样三个值:

  • V:要更新的变量(var)
  • E:预期值(expected)
  • N:新值(new)

比较并交换的过程如下:

判断 V 是否等于 E,如果等于,将 V 的值设置为 N;如果不等,说明已经有其它线程更新了 V,于是当前线程放弃更新,什么都不做。

这里的预期值 E 本质上指的是“旧值”

我们以一个简单的例子来解释这个过程:

  1. 如果有一个多个线程共享的变量i原本等于 5,我现在在线程 A 中,想把它设置为新的值 6;
  2. 我们使用 CAS 来做这个事情;
  3. 首先我们用 i 去与 5 对比,发现它等于 5,说明没有被其它线程改过,那我就把它设置为新的值 6,此次 CAS 成功,i的值被设置成了 6;
  4. 如果不等于 5,说明i被其它线程改过了(比如现在i的值为 2),那么我就什么也不做,此次 CAS 失败,i的值仍然为 2。

在这个例子中,i就是 V,5 就是 E,6 就是 N。

那有没有可能我在判断了i为 5 之后,正准备更新它的新值的时候,被其它线程更改了i的值呢?

不会的。因为 CAS 是一种原子操作,它是一种系统原语,是一条 CPU 的原子指令,从 CPU 层面已经保证它的原子性。

当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但

synchronized:

26 HashMap 内部节点是有序的吗?

无序的,根据 hash 值随机插入。

27 讲讲 LinkedHashMap 怎么实现有序的?

在HashMap的基础上,为插入的节点同时维持一个LinkList,并且是双向链表

从而实现插入的顺序或访问顺序。

28 讲讲 TreeMap 怎么实现有序的?

TreeMap的底层就是红黑树

TreeMap 通过 key 的比较器来决定元素的顺序,如果没有指定比较器,那么 key 必须实现 Comparable 接口。

TreeMap 的底层是红黑树,红黑树是一种自平衡的二叉查找树,每个节点都大于其左子树中的任何节点,小于其右子节点树种的任何节点。

基本上使用

TreeMap 底层维护了一棵红黑树。它依据 Key 实现的 Comparable 接口或者构造时传入的 Comparator 比较器来决定节点在树中的位置(左小右大)。当我们需要遍历时,它利用中序遍历算法,从而输出一个有序的 Key 集合。

使用场景:

当你需要 Key 始终保持有序,或者需要进行范围查找(如“查找 10 到 20 之间的所有用户”)时使用。

默认排序升序:

import java.util.TreeMap;

public class BasicDemo {
public static void main(String[] args) {
  // 1. 创建 TreeMap (默认按 Key 升序)
  TreeMap<Integer, String> map = new TreeMap<>();

  // 2. 乱序插入数据
  map.put(3, "张三");
  map.put(1, "李四");
  map.put(5, "王五");
  map.put(2, "赵六");

  // 3. 输出直接是有序的!
  // 结果: {1=李四, 2=赵六, 3=张三, 5=王五}
  System.out.println(map); 

  // 4. 获取元素
  System.out.println(map.get(3)); // 输出: 张三
}
}

自定义降序:

import java.util.Comparator;
import java.util.TreeMap;

public class ComparatorDemo {
public static void main(String[] args) {
  // 使用 Lambda 表达式定义比较规则:(o1, o2) -> o2 - o1 表示降序
  TreeMap<Integer, String> map = new TreeMap<>((k1, k2) -> k2 - k1);

  map.put(1, "A");
  map.put(3, "B");
  map.put(2, "C");

  // 结果: {3=B, 2=C, 1=A} (从大到小排好了)
  System.out.println(map);
}
}

这是 TreeMap 区别于 HashMap 最重要的地方。因为它是有序的,所以它能做很多 HashMap 做不到的事情。

假设我们要处理学生的成绩:

TreeMap<Integer, String> scores = new TreeMap<>();
scores.put(98, "A同学");
scores.put(60, "B同学");
scores.put(85, "C同学");
scores.put(45, "D同学");
// 当前顺序: {45=D, 60=B, 85=C, 98=A}

// --- 1. 获取极值 ---
System.out.println(scores.firstKey()); // 45 (最低分)
System.out.println(scores.lastKey());  // 98 (最高分)

// --- 2. 查找最近的值 (非常有用!) ---
// ceilingKey(80): 返回 >= 80 的最小 Key (天花板)
System.out.println(scores.ceilingKey(80)); // 85 (C同学)

// floorKey(80): 返回 <= 80 的最大 Key (地板)
System.out.println(scores.floorKey(80));   // 60 (B同学)

// --- 3. 截取子 Map (范围查询) ---
// subMap(from, to): 左闭右开 [60, 90)
// 获取 60 分到 90 分之间的所有学生
System.out.println(scores.subMap(60, 90)); // {60=B同学, 85=C同学}

// headMap(to): 小于 to 的所有数据
System.out.println(scores.headMap(60)); // {45=D同学} (不及格的)

// tailMap(from): 大于等于 from 的所有数据
System.out.println(scores.tailMap(85)); // {85=C同学, 98=A同学} (优秀的)

29 TreeMap 和 HashMap 的区别

  • HashMap 是基于数组+链表+红黑树实现的,put 元素的时候会先计算 key 的哈希值,然后通过哈希值计算出元素在数组中的存放下标,然后将元素插入到指定的位置,如果发生哈希冲突,会使用链表来解决,如果链表长度大于 8,会转换为红黑树。
  • TreeMap 是基于红黑树实现的,put 元素的时候会先判断根节点是否为空,如果为空,直接插入到根节点,如果不为空,会通过 key 的比较器来判断元素应该插入到左子树还是右子树。

在没有发生哈希冲突的情况下,HashMap 的查找效率是 O(1)。适用于查找操作比较频繁的场景。

TreeMap 的查找效率是 O(logn)。并且保证了元素的顺序,因此适用于需要大量范围查找或者有序遍历的场景。

特性HashMapLinkedHashMapTreeMap
底层结构数组+链表+红黑树HashMap + 双向链表红黑树
有序性无序插入顺序 (或访问顺序)Key 的大小顺序
实现原理Hash 算法链表记录前后关系二叉树的中序遍历
应用场景绝大多数场景需要记录缓存LRU/插入序需要按 Key 排序/范围查找

Set

30 讲讲 HashSet 的底层实现?

使用HashMap实现,只用key,不使用value,value会使用Object填充。HashSet 是由 HashMap 实现的,只不过值由一个固定的 Object 对象填充,而键用于操作。

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;
    private transient HashMap<E,Object> map;
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    // ……
}

实际开发中,HashSet 并不常用,比如,如果我们需要按照顺序存储一组元素,那么 ArrayList 和 LinkedList 更适合;如果我们需要存储键值对并根据键进行查找,那么 HashMap 可能更适合。

HashSet用于去重,比如,我们需要统计一篇文章中有多少个不重复的单词,就可以使用 HashSet 来实现。

// 创建一个 HashSet 对象
HashSet<String> set = new HashSet<>();

// 添加元素
set.add("沉默");
set.add("王二");
set.add("陈清扬");
set.add("沉默");

// 输出 HashSet 的元素个数
System.out.println("HashSet size: " + set.size()); // output: 3

// 遍历 HashSet
for (String s : set) {
    System.out.println(s);
}

HashSet 会自动去重,因为它是用 HashMap 实现的,HashMap 的键是唯一的,相同键会覆盖掉原来的键,于是第二次 add 一个相同键的元素会直接覆盖掉第一次的键。

(1)HashSet 和 ArrayList 的区别

  • 底层实现:ArrayList 是基于动态数组实现的,HashSet 是基于 HashMap 实现的。
  • 元素唯一性:ArrayList 允许重复元素和 null 值,可以有多个相同的元素;HashSet 保证每个元素唯一,不允许重复元素,基于元素的 hashCode 和 equals 方法来确定元素的唯一性。
  • 有序性:rrayList 保持元素的插入顺序,可以通过索引访问元素;HashSet 不保证元素的顺序,元素的存储顺序依赖于哈希算法,并且可能随着元素的添加或删除而改变。

(2)HashSet 怎么判断元素重复,重复了是否 put

HashSet 的 add 方法是通过调用 HashMap 的 put 方法实现的:

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

也就是说,HashSet 通过元素的哈希值来判断元素是否重复,如果重复了,会覆盖原来的值。

重复了还是要put

本文地址:https://www.yitenyun.com/4467.html

搜索文章

Tags

#服务器 #python #pip #conda #ios面试 #ios弱网 #断点续传 #ios开发 #objective-c #ios #ios缓存 #人工智能 #微信 #远程工作 #Trae #IDE #AI 原生集成开发环境 #Trae AI #kubernetes #笔记 #平面 #容器 #linux #学习方法 香港站群服务器 多IP服务器 香港站群 站群服务器 #运维 #学习 #银河麒麟高级服务器操作系统安装 #银河麒麟高级服务器V11配置 #设置基础软件仓库时出错 #银河麒高级服务器系统的实操教程 #生产级部署银河麒麟服务系统教程 #Linux系统的快速上手教程 #分阶段策略 #模型协议 #科技 #深度学习 #自然语言处理 #神经网络 #hadoop #hbase #hive #zookeeper #spark #kafka #flink #华为云 #部署上线 #动静分离 #Nginx #新人首发 #docker #fastapi #html #css #tcp/ip #网络 #qt #C++ #harmonyos #鸿蒙PC #物联网 #websocket #github #git #大数据 #职场和发展 #程序员创富 #进程控制 #经验分享 #安卓 #gemini #gemini国内访问 #gemini api #gemini中转搭建 #Cloudflare #PyTorch #模型训练 #星图GPU #Conda # 私有索引 # 包管理 #kylin #ARM服务器 # GLM-4.6V # 多模态推理 #低代码 #爬虫 #音视频 #开源 #arm #unity #c# #游戏引擎 #数信院生信服务器 #Rstudio #生信入门 #生信云服务器 #语言模型 #大模型 #ai #ai大模型 #agent #飞牛nas #fnos #word #umeditor粘贴word #ueditor粘贴word #ueditor复制word #ueditor上传word图片 #MobaXterm #ubuntu #内网穿透 #cpolar #ci/cd #jenkins #gitlab #node.js #langchain #数据库 #儿童书籍 #儿童诗歌 #童话故事 #经典好书 #儿童文学 #好书推荐 #经典文学作品 #ssh #flutter #开发语言 #云原生 #iventoy #VmWare #OpenEuler #前端 #nginx #后端 #serverless #diskinfo # TensorFlow # 磁盘健康 #Harbor #矩阵 #线性代数 #AI运算 #向量 #vscode #mobaxterm #计算机视觉 #ide #区块链 #测试用例 #生活 #RTP over RTSP #RTP over TCP #RTSP服务器 #RTP #TCP发送RTP #centos #svn #c++ #算法 #牛客周赛 #aws #云计算 #AI编程 #sql #AIGC #agi #android #腾讯云 #自动化 #ansible #分布式 #华为 #openHiTLS #TLCP #DTLCP #密码学 #商用密码算法 #fabric #postgresql #FTP服务器 #javascript #vue上传解决方案 #vue断点续传 #vue分片上传下载 #vue分块上传下载 #Reactor #http #项目 #高并发 #java-ee #文心一言 #AI智能体 #多个客户端访问 #IO多路复用 #回显服务器 #TCP相关API #缓存 #microsoft #PyCharm # 远程调试 # YOLOFuse #php #java #jar #Dell #PowerEdge620 #内存 #硬盘 #RAID5 #windows #flask #企业开发 #ERP #项目实践 #.NET开发 #C#编程 #编程与数学 #iBMC #UltraISO #pytorch #程序人生 #科研 #博士 #pycharm #鸿蒙 #架构 #网络协议 #安全 #jmeter #功能测试 #软件测试 #自动化测试 #mcu #mysql #散列表 #哈希算法 #数据结构 #leetcode #风控模型 #决策盲区 #数学建模 #2026年美赛C题代码 #2026年美赛 #uni-app #小程序 #notepad++ #spring boot #内存治理 #django #es安装 #vue.js #信息与通信 #rocketmq #DeepSeek #服务器繁忙 #AI #Ansible # 自动化部署 # VibeThinker #Ubuntu服务器 #硬盘扩容 #命令行操作 #VMware #web #webdav #课程设计 #dify #计算机网络 #spring cloud #spring #json #jvm #mmap #nio #golang #redis #蓝桥杯 #个人开发 #驱动开发 #mvp #设计模式 #游戏 #京东云 #性能优化 #我的世界 #jetty #web安全 #阻塞队列 #生产者消费者模型 #服务器崩坏原因 #数据仓库 #c语言 #LLM #vim #gcc #yum #MCP #MCP服务器 #鸭科夫 #逃离鸭科夫 #鸭科夫联机 #鸭科夫异地联机 #开服 #vllm #Streamlit #Qwen #本地部署 #AI聊天机器人 #prometheus #ecmascript #elementui #开源软件 #全能视频处理软件 #视频裁剪工具 #视频合并工具 #视频压缩工具 #视频字幕提取 #视频处理工具 #深度优先 #DFS #ffmpeg #企业微信 #Linux #TCP #线程 #线程池 #udp #Android #Bluedroid #智能手机 #钉钉 #机器人 #网络安全 #everything #阿里云 #AI论文写作工具 #学术论文创作 #论文效率提升 #MBA论文写作 #数据集 #单片机 #stm32 #嵌入式硬件 #信息可视化 #claude code #codex #code cli #ccusage #Ascend #MindIE #需求分析 #scala #测试工具 #压力测试 #adb #rabbitmq #protobuf #ModelEngine #设备驱动 #芯片资料 #网卡 #大模型学习 #AI大模型 #大模型教程 #大模型入门 #DisM++ # 系统维护 #gpu算力 #语音识别 #研发管理 #禅道 #禅道云端部署 #中间件 #n8n #架构师 #系统架构 #软考 #系统架构师 #守护进程 #复用 #screen #流量监控 #RAID #RAID技术 #磁盘 #存储 #MC #unity3d #服务器框架 #Fantasy #elasticsearch #几何学 #拓扑学 #链表 #链表的销毁 #链表的排序 #链表倒置 #判断链表是否有环 #智能路由器 #transformer #机器学习 #酒店客房管理系统 #毕设 #论文 #凤希AI伴侣 #生信 #java大文件上传 #java大文件秒传 #java大文件上传下载 #java文件传输解决方案 #webrtc #chatgpt #DS随心转 #mcp #mcp server #AI实战 #journalctl #LobeChat #vLLM #GPU加速 #RAG #全链路优化 #实战教程 #流程图 #论文阅读 #论文笔记 #毕业设计 #openresty #lua #wordpress #雨云 #Coze工作流 #AI Agent指挥官 #多智能体系统 #VS Code调试配置 #电脑 #vue3 #天地图 #403 Forbidden #天地图403错误 #服务器403问题 #天地图API #部署报错 #SSH反向隧道 # Miniconda # Jupyter远程访问 #grafana #SSH Agent Forwarding # PyTorch # 容器化 #todesk #asp.net大文件上传 #asp.net大文件上传下载 #asp.net大文件上传源码 #ASP.NET断点续传 #asp.net上传文件夹 #ping通服务器 #读不了内网数据库 #bug菌问答团队 #FL Studio #FLStudio #FL Studio2025 #FL Studio2026 #FL Studio25 #FL Studio26 #水果软件 #epoll #高级IO #debian #svm #amdgpu #kfd #ROCm #asp.net #面试 #1024程序员节 #claude #LoRA # RTX 3090 # lora-scripts #react.js #超算服务器 #算力 #高性能计算 #仿真分析工作站 #ddos #里氏替换原则 #幼儿园 #园长 #幼教 #fiddler #opencv #数据挖掘 #googlecloud #sizeof和strlen区别 #sizeof #strlen #计算数据类型字节数 #计算字符串长度 #银河麒麟 #系统升级 #信创 #国产化 #正则 #正则表达式 #arm开发 #Modbus-TCP #azure #编辑器 #金融 #金融投资Agent #Agent #AI写作 #iphone #ida #C语言 #WEB #制造 #个人博客 #ONLYOFFICE #MCP 服务器 #嵌入式 #laravel #apache #tomcat #程序员 #前端框架 #嵌入式编译 #ccache #distcc #shell #CPU利用率 #STUN # TURN # NAT穿透 #流媒体 #NAS #飞牛NAS #监控 #NVR #EasyNVR #cursor #数组 #信号处理 #目标跟踪 #ESXi #spine #进程 #操作系统 #进程创建与终止 #Shiro #反序列化漏洞 #CVE-2016-4437 #ollama #llm #RustDesk #IndexTTS 2.0 #本地化部署 #tcpdump #embedding #运营 #visual studio code #车辆排放 #产品经理 #ui #团队开发 #墨刀 #figma #oracle #SA-PEKS # 关键词猜测攻击 # 盲签名 # 限速机制 #树莓派4b安装系统 #搜索引擎 #我的世界服务器搭建 #minecraft #测试流程 #金融项目实战 #P2P #paddleocr #智慧校园解决方案 #智慧校园一体化平台 #智慧校园选型 #智慧校园采购 #智慧校园软件 #智慧校园专项资金 #智慧校园定制开发 #Spring AI #STDIO协议 #Streamable-HTTP #McpTool注解 #服务器能力 #CFD #时序数据库 #pencil #pencil.dev #设计 #sqlite #Playbook #AI服务器 #HeyGem # 远程访问 # 服务器IP配置 #simulink #matlab #边缘计算 #MS #Materials #Triton # CUDA #wsl #L2C #勒让德到切比雪夫 #selenium #海外服务器安装宝塔面板 #负载均衡 #SSH保活 #Miniconda #远程开发 #AB包 #autosar #openlayers #bmap #tile #server #vue #简单数论 #埃氏筛法 #openEuler #Hadoop #SSH # ProxyJump # 跳板机 #客户端 #DIY机器人工房 #vuejs #eBPF #.net #homelab #Lattepanda #Jellyfin #Plex #Emby #Kodi #推荐算法 #tensorflow #nacos #银河麒麟aarch64 #uvicorn #uvloop #asgi #event #zabbix #信令服务器 #Janus #MediaSoup #log #TensorRT # Triton # 推理优化 #Jetty # CosyVoice3 # 嵌入式服务器 #YOLO #建筑缺陷 #红外 #数码相机 #X11转发 #智能一卡通 #门禁一卡通 #梯控一卡通 #电梯一卡通 #消费一卡通 #一卡通 #考勤一卡通 #SMTP # 内容安全 # Qwen3Guard #sqlserver #改行学it #创业创新 #AI产品经理 #大模型开发 #求职招聘 #北京百思可瑞教育 #百思可瑞教育 #北京百思教育 #tdengine #涛思数据 #重构 #ms-swift # 一锤定音 # 大模型微调 #deepseek #risc-v #大语言模型 #长文本处理 #GLM-4 #Triton推理 #cpp #SSH公钥认证 # 安全加固 #数模美赛 #PowerBI #企业 #nas #Qwen3-14B # 大模型部署 # 私有化AI #screen 命令 #bash #状态模式 #macos #vp9 #ssm #ssl #支付 #远程桌面 #远程控制 #fpga开发 #LVDS #高速ADC #DDR #若依 #quartz #框架 # GLM-TTS # 数据安全 #银河麒麟操作系统 #openssh #华为交换机 #信创终端 #振镜 #振镜焊接 #llama #ceph #流量运营 #用户运营 #迁移重构 #数据安全 #漏洞 #代码迁移 #版本控制 #Git入门 #开发工具 #代码托管 #esp32教程 #目标检测 #模版 #函数 #类 #笔试 #蓝耘智算 #Anaconda配置云虚拟环境 #LabVIEW知识 #LabVIEW程序 #labview #LabVIEW功能 #MQTT协议 #OSS #firefox #vivado license #CVE-2025-68143 #CVE-2025-68144 #CVE-2025-68145 #rust #html5 #双指针 #chrome #RSO #机器人操作系统 #glibc #winscp #硬件工程 #智能体 #青少年编程 #政务 #集成学习 #集成测试 #https #微信小程序 #单元测试 #可信计算技术 #Fun-ASR # 硬件配置 # 语音识别 #算力一体机 #ai算力服务器 #powerbi #Clawdbot #个人助理 #数字员工 #SMP(软件制作平台) #EOM(企业经营模型) #应用系统 # 双因素认证 #自动驾驶 #rustdesk #p2p #连接数据库报错 #项目申报系统 #项目申报管理 #项目申报 #企业项目申报 #JAVA #Java #Docker #Canal #tornado #reactjs #web3 #源码 #闲置物品交易系统 #YOLOFuse # Base64编码 # 多模态检测 #IPv6 #DNS #bootstrap #SPA #单页应用 #web3.py #系统安全 #ipmitool #BMC #C #prompt #YOLOv8 # 目标检测 # Docker镜像 #麒麟OS #pdf #人脸识别 #人脸核身 #活体检测 #身份认证与人脸对比 #H5 #微信公众号 #swagger #IndexTTS2 # 阿里云安骑士 # 木马查杀 #1panel #vmware #5G #汇编 #mamba #mariadb #计算机 #学习笔记 #jdk #eclipse #servlet #LangGraph #CLI #Python #JavaScript #langgraph.json #CMake #Make #C/C++ #typescript #npm #策略模式 #模型上下文协议 #MultiServerMCPC #load_mcp_tools #load_mcp_prompt # 高并发部署 #vps #Anything-LLM #IDC服务器 #私有化部署 #raid #raid阵列 #电气工程 #C# #PLC # 水冷服务器 # 风冷服务器 #结构与算法 #webpack #intellij-idea #database #idea #Windows 更新 #学术写作辅助 #论文创作效率提升 #AI写论文实测 #TLS协议 #HTTPS #漏洞修复 #运维安全 #翻译 #开源工具 #maven #910B #rdp #扩展屏应用开发 #android runtime #能源 #ComfyUI # 推理服务器 # IndexTTS 2.0 # 远程运维 #libosinfo #Dify #ARM架构 #鲲鹏 #联机教程 #局域网联机 #局域网联机教程 #局域网游戏 #模拟退火算法 #微服务 #虚拟机 #考研 #软件工程 #产品运营 #windows11 #系统修复 #select #yolov12 #研究生life #文件传输 #电脑文件传输 #电脑传输文件 #电脑怎么传输文件到另一台电脑 #电脑传输文件到另一台电脑 #说话人验证 #声纹识别 #CAM++ #性能 #优化 #RAM #mongodb #cnn #其他 #PTP_1588 #gPTP #Windows #数据分析 #RXT4090显卡 #RTX4090 #深度学习服务器 #硬件选型 #gitea #浏览器自动化 #python #IntelliJ IDEA #Spring Boot #neo4j #NoSQL #SQL #OBC #idm #网站 #截图工具 #批量处理图片 #图片格式转换 #图片裁剪 #零售 #万悟 #联通元景 #镜像 #SSH免密登录 #结构体 #TCP服务器 #开发实战 #上下文工程 #langgraph #意图识别 #Android16 #音频性能实战 #音频进阶 #健身房预约系统 #健身房管理系统 #健身管理系统 #log4j #SSE # AI翻译机 # 实时翻译 #数据采集 #浏览器指纹 #clickhouse #代理 #平板 #交通物流 #智能硬件 #ESP32 #传感器 #MicroPython #3d #RK3576 #瑞芯微 #硬件设计 #CTF #gateway #Comate #edge #迭代器模式 #观察者模式 #twitter #scrapy #r-tree #聊天小程序 #arm64 #jupyter #无人机 #Deepoc #具身模型 #开发板 #未来 #串口服务器 #Modbus #MOXA #机器视觉 #6D位姿 #UOS #海光K100 #统信 #NFC #智能公交 #服务器计费 #FP-增长 #CANN #wpf # WebUI #mybatis #CUDA #交互 #UDP套接字编程 #UDP协议 #网络测试 #Proxmox VE #虚拟化 #硬件 #intellij idea #lvs #Host #渗透测试 #SSRF #部署 #GPU服务器 #8U #硬件架构 #昇腾300I DUO #NPU #vnstat #c++20 #音乐分类 #音频分析 #ViT模型 #Gradio应用 #鼠大侠网络验证系统源码 #cosmic #whisper #跨域 #发布上线后跨域报错 #请求接口跨域问题解决 #跨域请求代理配置 #request浏览器跨域 #运维开发 #opc ua #opc #游戏私服 #云服务器 #分类 #AutoDL #处理器 #黑群晖 #无U盘 #纯小白 #指针 #anaconda #虚拟环境 #SSH跳板机 # Python3.11 #东方仙盟 #游戏机 #JumpServer #堡垒机 #API限流 # 频率限制 # 令牌桶算法 #UDP的API使用 # REST API # GLM-4.6V-Flash-WEB #ip #Gunicorn #WSGI #Flask #并发模型 #容器化 #性能调优 #teamviewer #蓝湖 #Axure原型发布 #restful #ajax #微PE # GLM # 服务连通性 #视频去字幕 #flume #ambari #Socket网络编程 #turn #ai编程 #数据恢复 #视频恢复 #视频修复 #RAID5恢复 #流媒体服务器恢复 #muduo库 #uv #uvx #uv pip #npx #Ruff #pytest #零代码平台 #AI开发 #UDP #milvus #springboot #知识库 #昇腾 #pandas #matplotlib #web server #请求处理流程 #框架搭建 #SRS #直播 #聚类 #OPCUA #环境搭建 #ipv6 #duckdb #安恒明御堡垒机 #windterm #weston #x11 #x11显示服务器 #WinSCP 下载安装教程 #SFTP #FTP工具 #服务器文件传输 #excel #计算几何 #斜率 #方向归一化 #叉积 #高品质会员管理系统 #收银系统 #同城配送 #最好用的电商系统 #最好用的系统 #推荐的前十系统 #JAVA PHP 小程序 #samba #copilot # 批量管理 #ASR #SenseVoice #硬盘克隆 #DiskGenius #媒体 #ArkUI #ArkTS #鸿蒙开发 #手机h5网页浏览器 #安卓app #苹果ios APP #手机电脑开启摄像头并排查 #语音生成 #TTS #逻辑回归 #cesium #可视化 #IO #证书 #echarts #go #Rust #JNI #CPU ##程序员和算法的浪漫 #测评 #CCE #Dify-LLM #Flexus #Nacos # 数字人系统 # 远程部署 #宝塔面板部署RustDesk #RustDesk远程控制手机 #手机远程控制 #puppeteer #KMS #slmgr #TRO #TRO侵权 #TRO和解 #运维工具 #长文本理解 #glm-4 #推理部署 #Aluminium #Google #智能家居 #POC #问答 #交付 #动态规划 #社科数据 #数据统计 #经管数据 #xlwings #Excel #Discord机器人 #云部署 #程序那些事 #自由表达演说平台 #演说 #移动端h5网页 #调用浏览器摄像头并拍照 #开启摄像头权限 #拍照后查看与上传服务器端 #摄像头黑屏打不开问题 #nfs #iscsi #AI技术 #服务器IO模型 #非阻塞轮询模型 #多任务并发模型 #异步信号模型 #多路复用模型 # 黑屏模式 # TTS服务器 #前端开发 #领域驱动 #kmeans #Karalon #AI Test #RAGFlow #DeepSeek-R1 #贪心算法 #文件IO #输入输出流 #文件管理 #文件服务器 #国产开源制品管理工具 #Hadess #一文上手 #kong #Kong Audio #Kong Audio3 #KongAudio3 #空音3 #空音 #中国民乐 #范式 #React安全 #漏洞分析 #Next.js #ET模式 #非阻塞 #高并发服务器 #ICPC # 大模型 # 模型训练 #游戏程序 #scanf #printf #getchar #putchar #cin #cout #paddlepaddle #企业级存储 #网络设备 #iot #多模态 #微调 #超参 #LLamafactory #土地承包延包 #领码SPARK #aPaaS+iPaaS #数字化转型 #智能审核 #档案数字化 #Smokeping #农产品物流管理 #物流管理系统 #农产品物流系统 #农产品物流 #xss #pve #排序算法 #排序 #VMware Workstation16 #服务器操作系统 #Linux多线程 #zotero #WebDAV #同步失败 #代理模式 #工具集 #大模型应用 #API调用 #PyInstaller打包运行 #服务端部署 #ShaderGraph #图形 #VSCode # SSH #Langchain-Chatchat # 国产化服务器 # 信创 #软件 #本地生活 #电商系统 #商城 #2026AI元年 #年度趋势 #国产PLM #瑞华丽PLM #瑞华丽 #PLM #欧拉 #CSDN #aiohttp #asyncio #异步 #麒麟 #区间dp #二进制枚举 #图论 #.netcore # 自动化运维 #VoxCPM-1.5-TTS # 云端GPU # PyCharm宕机 #多线程 #性能调优策略 #双锁实现细节 #动态分配节点内存 #markdown #建站 #儿童AI #图像生成 #pjsip # 模型微调 #游戏美术 #技术美术 #游戏策划 #用户体验 #AI生成 # outputs目录 # 自动化 #ue5 #大学生 #大作业 #实体经济 #商业模式 #软件开发 #数智红包 #商业变革 #创业干货 #net core #kestrel #web-server #asp.net-core #elk #esp32 arduino #Zabbix #CosyVoice3 #语音合成 #插入排序 #HistoryServer #Spark #YARN #jobhistory #FASTMCP #ZooKeeper #ZooKeeper面试题 #面试宝典 #深入解析 #大模型部署 #mindie #大模型推理 #Chat平台 #业界资讯 #n8n解惑 #Go并发 #高并发架构 #Goroutine #系统设计 #Tracker 服务器 #响应最快 #torrent 下载 #2026年 #Aria2 可用 #迅雷可用 #BT工具通用 # 显卡驱动备份 #EMC存储 #存储维护 #NetApp存储 #测试覆盖率 #可用性测试 #TFTP #NSP #下一状态预测 #aigc #内存接口 # 澜起科技 # 服务器主板 #智慧城市 #eureka #海外短剧 #海外短剧app开发 #海外短剧系统开发 #短剧APP #短剧APP开发 #短剧系统开发 #海外短剧项目 #广播 #组播 #并发服务器 #x86_64 #数字人系统 #数字孪生 #三维可视化 #企业存储 #RustFS #对象存储 #高可用 #三维 #3D #三维重建 #WinDbg #Windows调试 #内存转储分析 #asp.net上传大文件 #随机森林 #rtsp #转发 #经济学 #编程 #c++高并发 #百万并发 #Termux #Samba #SSH别名 #PyTorch 特性 #动态计算图 #张量(Tensor) #自动求导Autograd #GPU 加速 #生态系统与社区支持 #与其他框架的对比 #cascadeur #设计师 #AI视频创作系统 #AI视频创作 #AI创作系统 #AI视频生成 #AI工具 #文生视频 #AI创作工具 #信创国产化 #达梦数据库 #CVE-2025-61686 #路径遍历高危漏洞 #AI+ #coze #AI入门 #AI赋能 #计组 #数电 #群晖 #音乐 #Node.js #漏洞检测 #CVE-2025-27210 #导航网 #Llama-Factory # 大模型推理 #uip # 代理转发 #GPU ##租显卡 #Xshell #Finalshell #生物信息学 #组学 #进程等待 #wait #waitpid # 服务器IP # 端口7860 #全文检索 #web服务器 #ThingsBoard MCP #可撤销IBE #服务器辅助 #私钥更新 #安全性证明 #双线性Diffie-Hellman # 公钥认证 # GPU租赁 # 自建服务器 #VibeVoice # 语音合成 # 云服务器 #统信UOS #win10 #qemu #HarmonyOS #遛狗 #VMWare Tool #MinIO服务器启动与配置详解 #ngrok #视觉检测 #visual studio #H5网页 #网页白屏 #H5页面空白 #资源加载问题 #打包部署后网页打不开 #HBuilderX #A2A #GenAI #磁盘配额 #存储管理 #形考作业 #国家开放大学 #系统运维 #自动化运维 #gRPC #注册中心 #Tokio #异步编程 #系统编程 #Pin #http服务器 #win11 #插件 #DHCP #C++ UA Server #SDK #跨平台开发 #机器人学习 # IP配置 # 0.0.0.0 #心理健康服务平台 #心理健康系统 #心理服务平台 #心理健康小程序 #SSH复用 # 远程开发 #galeweather.cn #高精度天气预报数据 #光伏功率预测 #风电功率预测 #高精度气象 #GATT服务器 #蓝牙低功耗 #DAG #线性回归 #c #服务器解析漏洞 #注入漏洞 #nvidia #safari #实时音视频 #贴图 #材质 #勒索病毒 #勒索软件 #加密算法 #.bixi勒索病毒 #数据加密 # ControlMaster #论文复现 #知识 #JT/T808 #车联网 #车载终端 #模拟器 #仿真器 #开发测试 #memory mcp #Cursor #网路编程 #AI赋能盾构隧道巡检 #开启基建安全新篇章 #以注意力为核心 #YOLOv12 #AI隧道盾构场景 #盾构管壁缺陷病害异常检测预警 #隧道病害缺陷检测 #IFix #openclaw # 远程连接 #Buck #NVIDIA #交错并联 #DGX #hibernate #攻防演练 #Java web #红队 # 树莓派 # ARM架构 #AITechLab #cpp-python #CUDA版本 #memcache #大剑师 #nodejs面试题 #C2000 #TI #实时控制MCU #AI服务器电源 #TTS私有化 # IndexTTS # 音色克隆 #ARM64 # DDColor # ComfyUI #Ubuntu #ESP32编译服务器 #Ping #DNS域名解析 #YOLO26 #YOLO11 #ranger #MySQL8.0 #GB28181 #SIP信令 #SpringBoot #视频监控 #WT-2026-0001 #QVD-2026-4572 #smartermail #连锁药店 #连锁店 #abtest # ARM服务器 # keep-alive #screen命令 #分布式数据库 #集中式数据库 #业务需求 #选型误 # Connection refused #智能体来了 #智能体对传统行业冲击 #行业转型 #系统管理 #服务 #Fluentd #Sonic #日志采集 #面向对象 #chat #Claude #clamav #源代码管理 #黑客技术 #网安应急响应 #管道Pipe #system V # 高并发 #appche #命令模式 #muduo #TcpServer #accept #dubbo #SAP #ebs #metaerp #oracle ebs #CogVideoX #AI部署 #图像处理 #yolo #SSH跳转 #postman # GPU集群 #AI-native #dba #LangFlow # 轻量化镜像 # 边缘计算 #国产化OS #远程连接 #汽车 #cpu #百度 #ueditor导入word #工程设计 #预混 #扩散 #燃烧知识 #层流 #湍流 #量子计算 # 批量部署 # 键鼠锁定 #mtgsig #美团医药 #美团医药mtgsig #美团医药mtgsig1.2 #opc模拟服务器 #后端框架 #服务器线程 # SSL通信 # 动态结构体 #RWK35xx #语音流 #实时传输 #node #报表制作 #职场 #数据可视化 #用数据讲故事 #TURN # WebRTC # HiChatBox #蓝牙 #LE Audio #BAP #composer #symfony #java-zookeeper #pxe #coffeescript #参数估计 #矩估计 #概率论 #寄存器 #MCP服务器注解 #异步支持 #方法筛选 #声明式编程 #自动筛选机制 #AI大模型应用开发 #可再生能源 #绿色算力 #风电 #麦克风权限 #访问麦克风并录制音频 #麦克风录制音频后在线播放 #用户拒绝访问麦克风权限怎么办 #uniapp 安卓 苹果ios #将音频保存本地或上传服务器 # child_process #ue4 #DedicatedServer #独立服务器 #专用服务器 #H3C #sentinel #AI应用编程 #dlms #dlms协议 #逻辑设备 #逻辑设置间权限 #r语言 #scikit-learn #安全威胁分析 #个性化推荐 #BERT模型 #仙盟创梦IDE #GLM-4.6V-Flash-WEB # AI视觉 # 本地部署 #gpt #网络攻击模型 #pyqt #语义搜索 #嵌入模型 #Qwen3 #AI推理 #STDIO传输 #SSE传输 #WebMVC #WebFlux #电商 #因果学习 #Minecraft #Minecraft服务器 #PaperMC #我的世界服务器 #EN4FE #tcp/ip #网络 #工业级串口服务器 #串口转以太网 #串口设备联网通讯模块 #串口服务器选型 #入侵 #日志排查 #人大金仓 #Kingbase #小艺 #搜索 #Spring AOP #高仿永硕E盘的个人网盘系统源码 #支持向量机 #图像识别 #多进程 #python技巧 #VPS #搭建 #递归 #线性dp #工程实践 #租显卡 #训练推理 #就业 #webgl #API #wps #音诺ai翻译机 #AI翻译机 # Ampere Altra Max #sklearn #Java程序员 #Java面试 #后端开发 #Spring源码 #Spring #轻量化 #低配服务器 #文本生成 #CPU推理 #国产操作系统 #V11 #kylinos #KMS激活 #poll #numpy #xml #Syslog #系统日志 #日志分析 #日志监控 #Autodl私有云 #深度服务器配置 #人形机器人 #人机交互 #人脸识别sdk #视频编解码 #域名注册 #新媒体运营 #网站建设 #国外域名 #HBA卡 #RAID卡 #挖漏洞 #攻击溯源 #DDD #tdd #stl #IIS Crypto #blender #warp #easyui #门禁 #梯控 #智能梯控 #电梯 #电梯运力 #电梯门禁 #程序开发 #程序设计 #计算机毕业设计 #Prometheus #idc #题解 #图 #dijkstra #迪杰斯特拉 #bond #服务器链路聚合 #网卡绑定 #编程助手 # GPU服务器 # tmux #Puppet # IndexTTS2 # TTS #智能制造 #供应链管理 #工业工程 #库存管理 #程序定制 #毕设代做 #课设 #交换机 #三层交换机 #高斯溅射 #性能测试 #LoadRunner #工厂模式 #树莓派 #N8N #云开发 #个人电脑 #AI智能棋盘 #Rock Pi S #RK3588 #RK3588J #评估板 #核心板 #嵌入式开发 #MC群组服务器 # Qwen3Guard-Gen-8B #CS2 #debian13 #BoringSSL #Moltbook #gpu #nvcc #cuda #漏洞挖掘 #Cpolar #国庆假期 #服务器告警 #unix #k8s #OpenManage #模块 # 权限修复 #ICE # 鲲鹏 #SQL注入主机 #http头信息 #Coturn #resnet50 #分类识别训练 #温湿度监控 #WhatsApp通知 #IoT #MySQL #Spire.Office # 离线AI #隐私合规 #网络安全保险 #法律风险 #风险管理 #React #Next #CVE-2025-55182 #RSC #SMARC #ARM #文件上传漏洞 #静脉曲张 #腿部健康 #Kylin-Server #服务器安装 #短剧 #短剧小程序 #短剧系统 #微剧 #远程访问 #远程办公 #飞网 #安全高效 #配置简单 # 智能运维 # 性能瓶颈分析 #快递盒检测检测系统 #空间计算 #原型模式 #nosql #devops #戴尔服务器 #戴尔730 #装系统 #junit #bug #数据访问 #vncdotool #链接VNC服务器 #如何隐藏光标 #FaceFusion # Token调度 # 显存优化 #WRF #WRFDA # 服务器IP访问 # 端口映射 #公共MQTT服务器 #wireshark #网络安全大赛 #FHSS #网络配置实战 #Web/FTP 服务访问 #计算机网络实验 #外网访问内网服务器 #Cisco 路由器配置 #静态端口映射 #网络运维 #CNAS #CMA #程序文件 #RPA #影刀RPA #AI办公 #lucene #视觉理解 #Moondream2 #多模态AI #nodejs #云服务器选购 #Saas #路由器 #outlook #错误代码2603 #无网络连接 #2603 #mssql #算力建设 #跳槽 #HarmonyOS APP #esb接口 #走处理类报异常 #CS336 #Assignment #Experiments #TinyStories #Ablation #b树 #具身智能 #SSH密钥 #CA证书 #练习 #基础练习 #循环 #九九乘法表 #计算机实现 #dynadot #域名 #ETL管道 #向量存储 #数据预处理 #DocumentReader #银河麒麟部署 #银河麒麟部署文档 #银河麒麟linux #银河麒麟linux部署教程 #声源定位 #MUSIC #windbg分析蓝屏教程 #le audio #低功耗音频 #通信 #连接 #星际航行 #mapreduce #agentic bi #smtp #smtp服务器 #PHP #ARMv8 #内存模型 #内存屏障 #fs7TF #娱乐 #敏捷流程 #ROS #Keycloak #Quarkus #AI编程需求分析 #canvas层级太高 #canvas遮挡问题 #盖住其他元素 #苹果ios手机 #安卓手机 #调整画布层级 #学术生涯规划 #CCF目录 #基金申请 #职称评定 #论文发表 #科研评价 #顶会顶刊 #AI 推理 #NV #npu #ServBay #安全架构 # OTA升级 # 黄山派 #内网 #ansys #ansys问题解决办法 #节日 # 网络延迟 #Kuikly #openharmony #远程软件 #SEO优化 #taro #代理服务器 #基础语法 #标识符 #常量与变量 #数据类型 #运算符与表达式 #地理 #遥感 #视频 #Archcraft #雨云服务器 #教程 #MCSM面板 #Apple AI #Apple 人工智能 #FoundationModel #Summarize #SwiftUI #Linly-Talker # 数字人 # 服务器稳定性 #主板 #总体设计 #电源树 #框图 #工作 #超时设置 #客户端/服务器 #网络编程 #挖矿 #Linux病毒 #sql注入 #传统行业 #实在Agent # 服务器配置 # GPU #react native #gnu #Gateway #认证服务器集成详解 #服务器开启 TLS v1.2 #IISCrypto 使用教程 #TLS 协议配置 #IIS 安全设置 #服务器运维工具 #uniapp #合法域名校验出错 #服务器域名配置不生效 #request域名配置 #已经配置好了但还是报错 #uniapp微信小程序 #glances #电子电气架构 #系统工程与系统架构的内涵 #Routine #华为od #华为机试 #强化学习 #策略梯度 #REINFORCE #蒙特卡洛 #Socket #套接字 #I/O多路复用 #字节序 #L6 #L10 #L9 # ms-swift #PN 结 #阿里云RDS #超算中心 #PBS #lsf #反向代理 #vrrp #脑裂 #keepalived主备 #高可用主备都持有VIP #软件需求 #adobe #数据迁移 #系统安装 #铁路桥梁 #DIC技术 #箱梁试验 #裂纹监测 #四点弯曲 #MinIO #express #cherry studio #gmssh #宝塔 #Exchange #free #vmstat #sar #AI Agent #开发者工具 #Qwen3-VL # 服务状态监控 # 视觉语言模型 #边缘AI # Kontron # SMARC-sAMX8 #职场发展 #隐函数 #常微分方程 #偏微分方程 #线性微分方程 #线性方程组 #非线性方程组 #复变函数 #okhttp #计算机外设 #UDP服务器 #recvfrom函数 #remote-ssh #健康医疗 #Ward #AI应用 #思爱普 #SAP S/4HANA #ABAP #NetWeaver #高考 #claude-code #高精度农业气象 #日志模块 #bigtop #hdp #hue #kerberos #Beidou #北斗 #SSR #WAN2.2 #4U8卡 AI 服务器 ##AI 服务器选型指南 #GPU 互联 #GPU算力 #信息安全 #信息收集 #dash #docker安装seata #生产服务器问题查询 #日志过滤 #统信操作系统 #VMware创建虚拟机 #远程更新 #缓存更新 #多指令适配 #物料关联计划 # AI部署 #材料工程 #智能电视 #m3u8 #HLS #移动端H5网页 #APP安卓苹果ios #监控画面 直播视频流 #决策树 #DooTask #sglang #防毒面罩 #防尘面罩 #数据报系统 #UEFI #BIOS #Legacy BIOS #效率神器 #办公技巧 #自动化工具 #Windows技巧 #打工人必备 #开关电源 #热敏电阻 #PTC热敏电阻 #旅游 #身体实验室 #健康认知重构 #系统思维 #微行动 #NEAT效应 #亚健康自救 #ICT人 #KMS 激活 # 服务器迁移 # 回滚方案 #dreamweaver #晶振 #云计算运维 #西门子 #汇川 #Blazor #运维 #夏天云 #夏天云数据 #hdfs #华为od机试 #华为od机考 #华为od最新上机考试题库 #华为OD题库 #华为OD机试双机位C卷 #od机考题库 #银河麒麟服务器系统 #Python3.11 #clawdbot #AI工具集成 #容器化部署 #分布式架构 #Matrox MIL #二次开发 #I/O模型 #并发 #水平触发、边缘触发 #多路复用 #vertx #vert.x #vertx4 #runOnContext #CMC #单例模式 #懒汉式 #恶汉式 # DIY主机 # 交叉编译 #防火墙 #0day漏洞 #DDoS攻击 #漏洞排查 #实时检测 #卷积神经网络 #密码 #基金 #股票 #AI电商客服 #余行补位 #意义对谈 #余行论 #领导者定义计划 #spring ai #oauth2 #nmodbus4类库使用教程 #docker-compose #rtmp # 高温监控 #ossinsight #AE # 局域网访问 # 批量处理 #rag #测速 #iperf #iperf3 #gerrit # 环境迁移 #cocos2d #图形渲染 #分子动力学 #化工仿真 #xshell #host key #小智 #rsync # 数据同步 #游戏服务器断线 #期刊 #SCI #外卖配送 #claudeCode #content7 #odoo #boltbot # 串口服务器 # NPort5630 #榛樿鍒嗙被 #Python办公自动化 #Python办公 #人脸活体检测 #live-pusher #动作引导 #张嘴眨眼摇头 #苹果ios安卓完美兼容 #ftp #sftp #YOLO识别 #YOLO环境搭建Windows #YOLO环境搭建Ubuntu #OpenHarmony #运动 #tekton #新浪微博 #传媒 #DuckDB #协议 #Tetrazine-Acid #1380500-92-4 #OpenAI #故障 #Arduino BLDC #核辐射区域探测机器人 #二值化 #Canny边缘检测 #轮廓检测 #透视变换 #esp32 #mosquito #FRP #2025年 #AI教程 #自动化巡检 #istio #服务发现 #jquery #fork函数 #进程创建 #进程终止 #moltbot #session #JADX-AI 插件 #语义检索 #向量嵌入 #starrocks