Java 集合框架:Java 中的双端队列 ArrayDeque 的实现

news2025/2/20 21:56:46

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 019 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自己的技术栈的同学。与此同时,本专栏的所有文章,也都会准备充足的代码示例和完善的知识点梳理,因此也十分适合零基础的小白和要准备工作面试的同学学习。当然,我也会在必要的时候进行相关技术深度的技术解读,相信即使是拥有多年 Java 开发经验的从业者和大佬们也会有所收获并找到乐趣。

在 Java 编程中,集合框架提供了一系列强大的数据结构来处理各种常见的数据存储和操作需求。其中,双端队列(Deque, Double-ended Queue)是一种灵活的数据结构,它允许在队列的两端进行元素的插入和移除。ArrayDeque 是 Java 集合框架中的一个重要实现,提供了高效的双端操作功能,兼具队列和栈的特性。

ArrayDeque 通过循环数组来实现队列操作,这种设计使得它在执行插入和删除操作时具有卓越的性能。与传统的 LinkedList 相比,ArrayDeque 在内存使用和性能上具有显著优势,尤其是在频繁进行头部和尾部操作时。它避免了 LinkedList 中节点的频繁分配和回收,并且通过数组的循环使用来最大限度地减少了空间浪费。

本文将详细介绍 ArrayDeque 的实现细节,包括其内部数据结构、核心方法的工作原理以及性能优化策略。我们将探讨 ArrayDeque 如何高效地支持双端操作,以及在实际开发中如何利用这一数据结构来优化应用程序的性能和资源使用。通过对 ArrayDeque 的深入分析,读者将能够更好地理解双端队列的运作机制,并在实际项目中充分利用这一强大的数据结构。


文章目录

      • 1、ArrayDeque 概述
        • 1.1、ArrayDeque 介绍
        • 1.2、ArrayDeque 特点
        • 1.3、ArrayDeque 用法
      • 2、ArrayDeque 底层实现
        • 2.1、ArrayDeque 数据结构
        • 2.2、插入操作
        • 2.3、推出操作
      • 3、ArrayDeque 的使用
        • 3.1、ArrayDeque 使用示例
        • 3.2、ArrayDeque 主要方法


1、ArrayDeque 概述

1.1、ArrayDeque 介绍

Deque 接口表示一个双端队列(Double Ended Queue),允许在队列的首尾两端操作,所以既能实现队列行为,也能实现栈行为。Deque 常用的两种实现 ArrayDeque 和 LinkedList。

ArrayDeque 是 Java 集合中双端队列的数组实现,可以从两端进行插入或删除操作,当需要使用栈时,Java 已不推荐使用 Stack,而是推荐使用更高效的ArrayDeque,当需要使用队列时也可以使用 ArrayDeque。

1.2、ArrayDeque 特点

ArrayDeque 有着如下的主要特性:

  • 双端队列: ArrayDeque 允许在两端(头部和尾部)进行高效的插入和删除操作;
  • 可变大小数组: ArrayDeque 使用一个可变大小的数组来存储元素,当数组满了时,会自动扩展其容量;
  • 无容量限制: 与 ArrayList 类似,ArrayDeque 没有固定的容量限制,可以根据需要动态扩展;
  • 高效操作: 在头部和尾部进行的插入和删除操作通常比 LinkedList 更高效,因为它没有涉及节点的额外开销。
1.3、ArrayDeque 用法

ArrayDeque 典型用法如下:

  1. 作为栈 (Stack): 可以使用 ArrayDeque 作为栈来使用,因为它提供了 pushpop 方法;
  2. 作为队列 (Queue): 也可以将 ArrayDeque 用作队列,因为它提供了 offerpoll 等方法。

例子:

import java.util.ArrayDeque;
import java.util.Deque;

public class ArrayDequeExample {
    public static void main(String[] args) {
        // 创建一个 ArrayDeque 实例
        Deque<String> deque = new ArrayDeque<>();
        // 添加元素
        deque.add("Element 1");
        deque.addFirst("Element 2");
        deque.addLast("Element 3");
        // 访问元素
        System.out.println("Head: " + deque.peek());
        System.out.println("Tail: " + deque.peekLast());
        // 删除元素
        System.out.println("Removed Head: " + deque.removeFirst());
        System.out.println("Removed Tail: " + deque.removeLast());
        // 栈操作
        deque.push("Stack Element 1");
        deque.push("Stack Element 2");
        System.out.println("Popped from stack: " + deque.pop());
    }
}

2、ArrayDeque 底层实现

2.1、ArrayDeque 数据结构

ArrayDeque 是 Java 集合框架中双端队列 (Deque) 的一种实现,基于可变大小的数组来存储元素。其数据结构设计使得在队列两端进行插入和删除操作非常高效:

  • 基础数组:ArrayDeque 内部使用一个数组来存储元素。这个数组是可变大小的,默认容量为 16。当元素数量超过当前容量时,数组会自动扩展;
  • 头部和尾部指针: ArrayDeque 通过两个指针来追踪队列的头部和尾部。这两个指针分别称为 headtail。初始情况下,headtail 都指向数组的第一个位置。
public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
{
    /**
     * 用于存储双端队列元素的数组。
     * 双端队列的容量是该数组的长度,这个长度总是2的幂。
     * 除了在某个addX方法内瞬时变满之外,这个数组永远不会变满,
     * 一旦变满(参见doubleCapacity方法),就会立即调整大小,从而避免
     * 头指针和尾指针绕回并相等。我们还保证所有不持有双端队列元素的数组单元总是为null。
     */
    transient Object[] elements; // 非private以简化嵌套类访问

    /**
     * 双端队列头部元素的索引(即remove()或pop()将要移除的元素的索引);
     * 如果双端队列为空,则为等于tail的任意数。
     */
    transient int head;

    /**
     * 下一个元素将被添加到双端队列尾部的索引(通过addLast(E)、add(E)或push(E))。
     */
    transient int tail;

    /**
     * 用于新创建的双端队列的最小容量。
     * 必须是2的幂。
     */
    private static final int MIN_INITIAL_CAPACITY = 8;
  
  	    public ArrayDeque() {
        elements = new Object[16];
    }

    public ArrayDeque(int numElements) {
        // 调用 allocateElements 方法分配数组空间
        // 传入的 numElements 参数决定了初始容量
        allocateElements(numElements);
    }

    public ArrayDeque(Collection<? extends E> c) {
        // 调用 allocateElements 方法分配数组空间
        // 传入集合 c 的大小决定了初始容量
        allocateElements(c.size());
        // 将集合 c 中的所有元素添加到当前双端队列中
        addAll(c);
    }

    private static int calculateSize(int numElements) {
        // 初始容量设置为最小初始容量
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // 如果 numElements 大于或等于初始容量,则计算需要的容量
        if (numElements >= initialCapacity) {
            // 将 initialCapacity 设置为 numElements 的值
            initialCapacity = numElements;
            // 通过位运算将容量扩展到大于或等于 numElements 的最小2的幂次方
            initialCapacity |= (initialCapacity >>> 1);
            initialCapacity |= (initialCapacity >>> 2);
            initialCapacity |= (initialCapacity >>> 4);
            initialCapacity |= (initialCapacity >>> 8);
            initialCapacity |= (initialCapacity >>> 16);
            // 将容量扩大到下一个2的幂次方
            initialCapacity++;
            // 如果计算出的容量小于0,说明容量过大,避免超出数组的最大容量
            if (initialCapacity < 0)
                // 将容量减半,以避免分配过大的内存
                initialCapacity >>>= 1;
        }
        // 返回计算得到的容量
        return initialCapacity;
    }

    private void allocateElements(int numElements) {
        // 根据 calculateSize 方法计算的容量来创建数组
        elements = new Object[calculateSize(numElements)];
    }

  	// 省略其他方法和实现细节
  	...
}

详细解读:

  1. elements:这个数组用于存储 ArrayDeque 中的所有元素。数组的长度始终是 2 的幂,确保了高效的取模操作(通过位运算实现)。除了在某些方法中瞬时变满之外,数组永远不会变满。当数组变满时,ArrayDeque 会立即调用 doubleCapacity 方法来扩展容量,以避免头尾指针相等的情况;
  2. head:这是一个指向双端队列头部的索引,即最先添加的元素的索引。remove()pop() 方法将会移除该索引处的元素。当双端队列为空时,这个索引将等于 tail
  3. tail:这是一个指向双端队列尾部的索引,即下一个元素将要添加到的索引。addLast(E)add(E)push(E) 方法会在这个索引处添加元素;
  4. MIN_INITIAL_CAPACITY:这是 ArrayDeque 初始容量的最小值,必须是2的幂。这个值定义了新创建的 ArrayDeque 的初始容量,确保在容量扩展前有足够的空间来存储元素;
  5. 默认构造器会将 elements 初始化为一个长度为 16 的数组,另外两个构造器将根据参数的长度来初始化,最终会调用calculateSize方法计算初始长度。

这里需要注意一点,用 elements 存储的是双端队列,headtail 参数表示双端队列的头和尾的索引,但并不意味着 head 值永远小于 tail,当 tail 移动至数组末尾,但队列长度小于数组时(意味着此时 head大 于0),再向队列末尾添加数据将会使 tail 移动至数组的开头。

假设下图为当前的 elements 数组,黄色方块表示其中有数据:

img

当我们向队列末尾添加一个元素时,数组变成了下面这样:

img

目前都是按照我们预期发展的,现在我们来调用poll方法移除队列头部的一个元素,此时elements变成了下图:

img

这个时候因为我们移除了队列的头部元素,数组的开头已经空下来了一个位置,这时候再向队列末尾追加一个元素。

img

可以看到,此时 headtail 的右侧,ArrayDeque 为了不浪费数组空间进行了这样的设计,也不难理解。

2.2、插入操作

addFirst(E e)addLast(E e) 两个方法分别在头部和尾部添加元素,并在必要时调用 doubleCapacity() 方法扩展容量。

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
{

  	// 省略其他方法和实现细节
  	...
  	
    /**
     * 将指定的元素插入到此双端队列的前面。
     *
     * @param e 要添加的元素
     * @throws NullPointerException 如果指定的元素为 null
     */
    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        // 计算新的 head 索引,并将元素 e 插入到新的 head 位置
        elements[head = (head - 1) & (elements.length - 1)] = e;
        // 如果 head 与 tail 相等,表示数组已满,调用 doubleCapacity 方法扩展容量
        if (head == tail)
            doubleCapacity();
    }

    /**
     * 将指定的元素插入到此双端队列的末尾。
     *
     * <p>此方法等效于 {@link #add}.
     *
     * @param e 要添加的元素
     * @throws NullPointerException 如果指定的元素为 null
     */
    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        // 将元素 e 插入到 tail 位置
        elements[tail] = e;
        // 计算新的 tail 索引,并进行循环数组操作
        if ((tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }

    /**
     * 扩展此双端队列的容量。仅在满时调用,即当 head 和 tail
     * 环绕相等时。
     */
    private void doubleCapacity() {
        assert head == tail;
        int p = head;
        int n = elements.length;
        int r = n - p; // p右侧的元素个数
        int newCapacity = n << 1; // 新容量为当前容量的两倍
        if (newCapacity < 0)
            throw new IllegalStateException("对不起,双端队列太大了");
        Object[] a = new Object[newCapacity];
        // 将元素从原数组复制到新数组
        System.arraycopy(elements, p, a, 0, r);
        System.arraycopy(elements, 0, a, r, p);
        elements = a;
        // 更新 head 和 tail 指针
        head = 0;
        tail = n;
    }
  	
  	// 省略其他方法和实现细节
  	...
}

2.3、推出操作

ArrayDequepollremove 相关方法通过循环数组实现高效的双端操作。pollFirst()pollLast() 方法尝试从队列的前端或末尾移除并返回元素,如果队列为空则返回 null;而 removeFirst()removeLast() 方法在移除元素时会检查队列是否为空,若为空则抛出 NoSuchElementException 异常。这些方法通过更新数组的 headtail 指针来维护队列状态,并在移除元素后将相应的数组槽置为空,以便进行垃圾回收。

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
{
  	// 省略其他方法和实现细节
  	...

		/**
     * 从队列的前面移除并返回第一个元素。
     *
     * @throws NoSuchElementException 如果队列为空
     */
    public E removeFirst() {
        // 调用 pollFirst 方法尝试移除并返回第一个元素
        E x = pollFirst();
        // 如果 pollFirst 返回 null,则抛出 NoSuchElementException 异常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    /**
     * 从队列的末尾移除并返回最后一个元素。
     *
     * @throws NoSuchElementException 如果队列为空
     */
    public E removeLast() {
        // 调用 pollLast 方法尝试移除并返回最后一个元素
        E x = pollLast();
        // 如果 pollLast 返回 null,则抛出 NoSuchElementException 异常
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

    public E pollFirst() {
        int h = head;
        @SuppressWarnings("unchecked")
        // 获取并强制转换头部的元素
        E result = (E) elements[h];
        // 如果头部的元素为 null,表示队列为空,直接返回 null
        if (result == null)
            return null;
        // 置空头部的元素槽
        elements[h] = null;
        // 更新 head 索引,移到下一个位置
        head = (h + 1) & (elements.length - 1);
        return result;
    }

    public E pollLast() {
        int t = (tail - 1) & (elements.length - 1);
        @SuppressWarnings("unchecked")
        // 获取并强制转换尾部的元素
        E result = (E) elements[t];
        // 如果尾部的元素为 null,表示队列为空,直接返回 null
        if (result == null)
            return null;
        // 置空尾部的元素槽
        elements[t] = null;
        // 更新 tail 索引,移到前一个位置
        tail = t;
        return result;
    }
  
  	// 省略其他方法和实现细节
  	...
}

3、ArrayDeque 的使用

3.1、ArrayDeque 使用示例

ArrayDeque 使用循环数组来存储元素,当在数组头部添加元素时,如果当前头部指针在数组的起始位置(索引为 0),新元素将被插入到数组的最后一位。这是通过循环数组的特性实现的,确保在队列的两端进行操作时能够高效利用数组的空间。

假设我们有一个初始的 ArrayDeque,数组的初始状态如下:

[ 1, null, null, null, null, null, null, null ]  // head = 0, tail = 1

数组索引 0 位置有元素 1,head 指向 0,tail 指向 1。

现在,如果我们在队列前面添加一个元素 2,那么新元素将被插入到数组的最后一位(索引7),并且 head 指针将会更新:

deque.addFirst(2);

此时,数组和指针的状态如下:

[ 1, null, null, null, null, null, null, 2 ]  // head = 7, tail = 1

是的,ArrayDeque 使用循环数组来存储元素,当在数组头部添加元素时,如果当前头部指针在数组的起始位置(索引为 0),新元素将被插入到数组的最后一位。这是通过循环数组的特性实现的,确保在队列的两端进行操作时能够高效利用数组的空间。

现在,如果我们在队列前面添加一个元素2,那么新元素将被插入到数组的最后一位(索引7),并且 head 指针将会更新,下面是一个具体的代码示例:

import java.util.ArrayDeque;

public class ArrayDequeExample {
    public static void main(String[] args) {
        ArrayDeque<Integer> deque = new ArrayDeque<>(8);

        // 初始化添加一个元素
        deque.add(1);

        // 打印初始状态
        System.out.println("Initial deque: " + deque);

        // 在队列前面添加一个元素
        deque.addFirst(2);

        // 打印添加后的状态
        System.out.println("After adding to the front: " + deque);
    }
}

输出结果:

Initial deque: [1]
After adding to the front: [2, 1]
3.2、ArrayDeque 主要方法

ArrayDeque 主要方法:

  • add(E e): 将元素添加到队列尾部。
  • addFirst(E e): 将元素添加到队列头部。
  • addLast(E e): 将元素添加到队列尾部。
  • offer(E e): 尝试将元素添加到队列尾部。
  • offerFirst(E e): 尝试将元素添加到队列头部。
  • offerLast(E e): 尝试将元素添加到队列尾部。
  • remove(): 移除并返回队列头部的元素。
  • removeFirst(): 移除并返回队列头部的元素。
  • removeLast(): 移除并返回队列尾部的元素。
  • poll(): 检索并移除队列头部的元素,如果队列为空,则返回 null
  • pollFirst(): 检索并移除队列头部的元素,如果队列为空,则返回 null
  • pollLast(): 检索并移除队列尾部的元素,如果队列为空,则返回 null
  • peek(): 检索但不移除队列头部的元素,如果队列为空,则返回 null
  • peekFirst(): 检索但不移除队列头部的元素,如果队列为空,则返回 null
  • peekLast(): 检索但不移除队列尾部的元素,如果队列为空,则返回 null
  • push(E e): 将元素推送到栈顶。
  • pop(): 从栈顶弹出元素并返回。

ArrayDeque 是一个非常灵活且高效的双端队列实现,在需要频繁操作队列两端的场景下,ArrayDeque 是一个很好的选择。

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

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

相关文章

AI多模态实战教程:面壁智能MiniCPM-V多模态大模型问答交互、llama.cpp模型量化和推理

一、项目简介 MiniCPM-V 系列是专为视觉-语⾔理解设计的多模态⼤型语⾔模型&#xff08;MLLMs&#xff09;&#xff0c;提供⾼质量的⽂本输出&#xff0c;已发布4个版本。 1.1 主要模型及特性 &#xff08;1&#xff09;MiniCPM-Llama3-V 2.5&#xff1a; 参数规模: 8B性能…

初学Linux之常见指令(下)

初学Linux之常见指令&#xff08;下&#xff09; 文章目录 初学Linux之常见指令&#xff08;下&#xff09;1. echo 指令2. cat 指令3. more 指令4. less 指令5. head 和 tail 指令6. date 指令7. cal 指令8. which 指令9. alias 指令10. find 指令11. grep 指令12. zip 和 unz…

C++中如何高效拼接两个vector

在C编程中&#xff0c;vector是一种常用的数据结构&#xff0c;它代表了一个可以动态改变大小的数组。在实际开发中&#xff0c;经常需要将两个vector拼接在一起&#xff0c;形成一个新的vector。本文将详细介绍如何在C中拼接两个vector&#xff0c;并探讨不同方法的性能差异。…

初学51单片机之指针基础与串口通信应用

开始之前推荐一个电路学习软件&#xff0c;这个软件笔者也刚接触。名字是Circuit有在线版本和不在线版本&#xff0c;这是笔者在B站看视频翻到的。 Paul Falstadhttps://www.falstad.com/这是地址。 离线版本在网站内点这个进去 根据你的系统下载你需要的版本红线的是windows…

PMP--知识卡片--敏捷方法

文章目录 敏捷方法&#xff0c;是一种新型软件开发方法。不要求遵循传统的软件开发流程&#xff0c;强调快速开发和有效适应需求变化&#xff0c;典型代表如看板、Scrumban、极限编程、测试驱动开发等。 区别于传统项目场景&#xff0c;敏捷项目场景强调交互协作、尊重个体、面…

Linux系统快速搭建轻量化网站Halo并实现无公网IP远程访问

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

无价之美:大麗和和品牌美学概念宣传片发布

高级珠宝品牌大麗和和筹备6个月&#xff0c;隆重发布《无价之美》品牌美学概念宣传片。品牌创始人牟童女士携珍贵藏品&#xff0c;亲诉美与传承的故事。 “尊尚正美” 珍稀宝石的颜色&#xff0c;是以色正为美。“正”就是事物极致的样子。最受人追慕的翡翠被称为“帝王绿”&a…

drawio更改默认字体大小(暂时有问题,修改中)

PS&#xff1a;其他的也可以在这里修改对应的值

Docker无法拉取镜像!如何解决?

问题现象 继去年Docker Hub被xxx后&#xff0c;各大NAS的注册表均出现问题&#xff0c;例如群晖的Docker套件注册表无法连接&#xff08;更新至DSM7.2版本后恢复&#xff09;。而在今年2024年6月初&#xff08;约2024.06.06&#xff09;&#xff0c;NAS中最重要的工具Docker又…

解决git拉取代码报错:Couldn‘t connect to server

前言&#xff1a; 今天在拉取git仓库代码的时候&#xff0c;报错&#xff1a;fatal: unable to access https://codeup.aliyun.com/fly/business-project/lezhi-HR.git/: Failed to connect to 127.0.0.1 port 8020 after 2082 ms: Couldnt connect to server 错误截图&#…

mysql中的索引和分区

目录 1.编写目的 2.索引 2.1 创建方法 2.2 最佳适用 2.3 索引相关语句 3.分区 3.1 创建方法 3.2 最佳适用 Welcome to Code Blocks blog 本篇文章主要介绍了 [Mysql中的分区和索引] ❤博主广交技术好友&#xff0c;喜欢文章的可以关注一下❤ 1.编写目的 在MySQL中&…

函数调用过程

生成机器码.o文件&#xff0c;使用objdump - d -M intel hello_func.o来看汇编代码 栈内存由于历史原因看作是从高地址往低地址扩张所以栈底为高地址&#xff0c;栈顶为低地址。 rbp存储的时当前栈帧的基地址&#xff0c;栈底地址。 rsp存储的是栈顶地址&#xff0c;rip存储…

【计算机网络】TCP/IP——流量控制与拥塞控制

学习日期&#xff1a;2024.7.22 内容摘要&#xff1a;TCP的流量控制与拥塞控制 流量控制 一般来说&#xff0c;我们总是希望数据传输的快一些&#xff0c;但是如果数据传输的太快&#xff0c;接收方可能就来不及接收&#xff0c;这就会导致数据的丢失&#xff0c;流量控制正是…

【代码随想录】【算法训练营】【第58天 4】 [卡码104]建造最大岛屿

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 卡码网。 day 58&#xff0c;周四&#xff0c;ding~ 题目详情 [卡码104] 建造最大岛屿 题目描述 卡码104 建造最大岛屿 LeetCode类似题目827 最大人工岛 解题思路 前提&#xff1a; 思路&#xff1a; 重点…

压缩pdf大小的方法 指定大小软件且清晰

在数字化时代&#xff0c;pdf文件因其良好的兼容性和稳定性&#xff0c;已成为文档分享的主流格式。然而&#xff0c;高版本的pdf文件往往体积较大&#xff0c;传输和存储都相对困难。本文将为您详细介绍几种简单有效的方法&#xff0c;帮助您减小pdf文件的大小&#xff0c;让您…

基于STM32单片机生理监控心率脉搏TFT彩屏波形曲线

基于STM32单片机生理监控心率脉搏TFT彩屏波形曲线 1、系统功能介绍2、演示视频3、系统框图4、系统电路介绍4.1、STM32单片机最小系统设计4.2、按键电路设计4.3、蜂鸣器报警电路设计4.4、Pulsesensor脉搏心率传感器模块电路设计 5、程序设计5.1、LCD TFT屏幕初始化5.2、TFT屏幕显…

【性能优化】在大批量数据下使用 HTML+CSS实现走马灯,防止页面卡顿(一)

切换效果 页面结构变化 1.需求背景 项目首页存有一个小的轮播模块,保密原因大概只能这么展示,左侧图片右侧文字,后端一次性返回几百条数据(开发环境下,生产环境只会更多).无法使用分页解决,前端需要懒加载防止页面卡顿 写个小demo演示,如下 2.解决思路 获取到数据后,取第一…

《JavaSE》---21.<简单认识Java的集合框架包装类泛型>

目录 前言 一、什么是集合框架 1.1类和接口总览 二、集合框架的重要性 2.1 开发中的使用 2.2 笔试及面试题 三、背后所涉及的数据结构 3.1 什么是数据结构 3.2 容器背后对应的数据结构 四、包装类 4.1 基本数据类型和对应的包装类 4.2 装箱和拆箱 1.最初的写法 2.…

向量数据库|一文全面了解向量数据库的基本概念、原理、算法、选型

向量数据库的原理和实现&#xff0c;包括向量数据库的基本概念、相似性搜索算法、相似性测量算法、过滤算法和向量数据库的选型等等。向量数据库是崭新的领域&#xff0c;目前大部分向量数据库公司的估值乘着 AI 和 GPT 的东风从而飞速的增长&#xff0c;但是在实际的业务场景中…

NodeRed测试modbus RTU或modbus TCP通讯

目录标题 STEP1 添加modbus节点STEP2 查看是否安装成功STEP3 modbusTCP读取写入设置读取设置写入设置 STEP4 读写测试 STEP1 添加modbus节点 节点管理——控制板——安装 找到node-red-contrib-modbus&#xff0c;点击安装 STEP2 查看是否安装成功 安装成功后&#xff0c;左…