文章目录

  • 一、认识List接口
    • 1.1 List的定义与继承关系
    • 1.2 Collection接口的核心方法
    • 1.3 List接口的独特方法
  • 二、线性表与顺序表基础
    • 2.1 线性表
    • 2.2 顺序表
    • 自定义顺序表(MyArrayList)实现
      • 1. 前期准备:自定义异常类
      • 2. MyArrayList核心结构
      • 3. 工具方法:简化重复逻辑
        • (1)判断空表与容量满
        • (2)边界校验
        • (3)动态扩容
      • 4. 核心功能实现:增删改查
        • (1)添加元素
          • ① 尾插 add(int e)
          • ② 指定位置插入 add(int pos, int e)
        • (2)查询元素
          • ① 根据索引查元素
          • ② 根据元素查索引
          • ③ 判断元素是否存在
        • (3)修改元素(set(int pos, int e))
        • (4)删除元素(remove(int e))
        • (5)其他辅助功能
  • 三、ArrayList详解
    • 3.1 ArrayList的简介
    • 3.2 ArrayList的构造方法
    • 3.3 ArrayList的常见操作
    • 3.4 ArrayList的遍历方式
    • 3.5 ArrayList的扩容机制
    • 3.6 ArrayList的问题与思考

一、认识List接口

1.1 List的定义与继承关系

在Java集合框架里,List是一个接口,它继承自Collection接口。而Collection接口又继承自Iterable接口,这就决定了List具备了Collection接口规范的常用方法,同时也拥有了可逐个元素遍历的特性。

从数据结构角度来看,List本质上是一个线性表,即由n个具有相同类型元素组成的有限序列。在这个序列上,我们可以进行元素的增删改查以及遍历等操作,这为数据的有序管理提供了便利。

1.2 Collection接口的核心方法

Collection接口作为List的父接口,定义了一系列容器常用方法,这些方法在List中也得到了实现和扩展,具体如下:

  • size():返回集合中元素的个数,类型为int。
  • contains(Object o):判断集合中是否包含指定元素o,返回值为boolean类型。
  • iterator():返回一个用于遍历集合元素的Iterator对象。
  • toArray():将集合中的元素转换为一个Object类型的数组。
  • toArray(T[] a):将集合中的元素转换为指定类型T的数组。
  • add(E e):向集合中添加元素e,添加成功返回true,否则返回false。
  • remove(Object o):从集合中删除指定元素o,删除成功返回true,否则返回false。
  • containsAll(Collection<?> c):判断集合是否包含另一个集合c中的所有元素,返回boolean类型。
  • addAll(Collection<? extends E> c):将另一个集合c中的所有元素添加到当前集合中,添加成功返回true。
  • removeAll(Collection<?> c):从当前集合中删除所有存在于集合c中的元素,删除成功返回true。
  • removeIf(Predicate<? super E> filter):根据指定的过滤条件filter,删除集合中满足条件的元素,返回boolean类型。
  • retainAll(Collection<?> c):保留当前集合中与集合c共有的元素,删除其他元素,操作成功返回true。
  • clear():清空集合中的所有元素。
  • equals(Object o):判断当前集合与指定对象o是否相等,返回boolean类型。
  • hashCode():返回当前集合的哈希值。
  • spliterator():返回一个用于分割集合元素的Spliterator对象。
  • stream():返回一个用于遍历集合元素的顺序流Stream。
  • parallelStream():返回一个用于并行遍历集合元素的并行流Stream。
  • isEmpty():判断集合是否为空,返回boolean类型。

1.3 List接口的独特方法

除了继承自Collection接口的方法外,List接口还根据线性表的特性,新增了一些独特的方法,以满足对元素进行更精细操作的需求,常用方法如下表所示:

方法解释
boolean add(E e)在List的末尾插入元素e
void add(int index, E element)将元素element插入到List中指定的index位置
boolean addAll(Collection<? extends E> c)将集合c中的所有元素添加到List的末尾
E remove(int index)删除List中index位置的元素,并返回被删除的元素
boolean remove(Object o)删除List中遇到的第一个指定元素o,删除成功返回true
E get(int index)获取List中下标为index位置的元素
E set(int index, E element)将List中下标为index位置的元素设置为element,并返回该位置原来的元素
void clear()清空List中的所有元素
boolean contains(Object o)判断元素o是否存在于List中,存在返回true
int indexOf(Object o)返回元素o在List中第一次出现的下标,若不存在则返回-1
int lastIndexOf(Object o)返回元素o在List中最后一次出现的下标,若不存在则返回-1
List<E> subList(int fromIndex, int toIndex)截取List中从fromIndex(包含)到toIndex(不包含)的部分元素,组成一个新的List返回

需要注意的是,List是一个接口,不能直接实例化使用。如果要使用List,必须实例化它的实现类,在Java集合框架中,ArrayListLinkedList是List接口的常用实现类,本文后续将重点介绍ArrayList。

二、线性表与顺序表基础

在深入了解ArrayList之前,我们先来回顾一下线性表和顺序表的相关概念,这是理解ArrayList底层原理的基础。

2.1 线性表

线性表(linear list)是由n个具有相同特性的数据元素组成的有限序列,它是一种在实际应用中广泛使用的数据结构。常见的线性表包括顺序表、链表、栈、队列等。

线性表在逻辑上呈现为线性结构,就像一条连续的直线,元素之间存在着一对一的相邻关系。但在物理存储结构上,线性表并不一定是连续的,它通常以数组和链式结构这两种形式进行存储。

2.2 顺序表

顺序表是线性表的一种常见实现方式,它使用一段物理地址连续的存储单元来依次存储数据元素,一般情况下是通过数组来实现的。在顺序表中,我们可以基于数组完成数据的增删查改等操作。

自定义顺序表(MyArrayList)实现

1. 前期准备:自定义异常类

为了更清晰地处理“空表操作”和“非法索引”这两类错误,我们定义两个运行时异常(继承自RuntimeException):

  • 空表异常:当操作空表时抛出
public class EmptyException extends RuntimeException {public EmptyException(String message) {super(message);}
}
  • 索引非法异常:当索引超出有效范围时抛出

public class PosIllegalException extends RuntimeException {public PosIllegalException(String message) {super(message);}
}

2. MyArrayList核心结构

首先定义顺序表的成员变量和构造方法,完成初始初始化:

import java.util.Arrays;public class MyArrayList {// 1. 成员变量private static final int DEFAULT_CAPACITY = 10; // 默认初始容量(10)private int[] array; // 底层数组:存储数据private int size = 0; // 有效元素个数:初始为0(空表)// 2. 构造方法:默认初始化(容量10)public MyArrayList() {this.array = new int[DEFAULT_CAPACITY];}
}

3. 工具方法:简化重复逻辑

先实现两个通用工具方法,避免后续操作中重复编写边界校验代码:

(1)判断空表与容量满
  • 判断是否为空表
public boolean isEmpty() {return size == 0;
}
  • 判断容量是否已满
public boolean isFull() {return size == array.length;
}
(2)边界校验
  • 校验索引合法性:pos必须在 [0, size) 范围内
public void checkPos(int pos) throws PosIllegalException {if (pos < 0 || pos >= size) {throw new PosIllegalException("索引非法!当前有效元素个数:" + size + ",传入索引:" + pos);}
}
  • 校验是否为空表:空表不允许执行“获取元素”“删除元素”等操作
public void checkEmpty() throws EmptyException {if (isEmpty()) {throw new EmptyException("操作失败!当前顺序表为空");}
}
(3)动态扩容

当容量满(isFull() == true)时,通过Arrays.copyOf()将数组容量扩大为原来的2倍,实现“动态增长”:

public void grow() {// 扩容逻辑:新数组容量 = 原容量 * 2this.array = Arrays.copyOf(this.array, this.array.length * 2);System.out.println("扩容成功!新容量:" + this.array.length);
}

4. 核心功能实现:增删改查

(1)添加元素

添加元素分为两种场景:尾插(默认添加到表尾)和指定位置插入(插入到索引pos处)。

① 尾插 add(int e)

先判断是否满容量,满则扩容;再将元素放入array[size],最后size++(有效元素个数+1)。

public void add(int e) {if (isFull()) {grow(); }array[size] = e; size++; 
}
② 指定位置插入 add(int pos, int e)

先校验索引合法性,再判断是否扩容;然后将[pos, size-1]的元素向后移动1位,最后在pos处放入元素并size++

public void add(int pos, int e) {try {checkPos(pos); if (isFull()) {grow(); }// 元素后移:从最后一个元素(size-1)开始,到pos结束,依次向后移1位for (int i = size - 1; i >= pos; i--) {array[i + 1] = array[i];}array[pos] = e; size++; } catch (PosIllegalException e) {e.printStackTrace(); }
}
(2)查询元素

查询分为两种场景:根据索引查元素(getElement(int pos))和根据元素查索引(indexOf(int e)),以及判断元素是否存在(contains(int toFind))。

① 根据索引查元素

先校验空表和索引合法性,再直接返回array[pos](随机访问,O(1))。

public int getElement(int pos) {try {checkEmpty(); checkPos(pos);return array[pos]; } catch (EmptyException | PosIllegalException e) {e.printStackTrace();}return -1; // 异常时返回默认值}
② 根据元素查索引

遍历[0, size-1]的元素,找到第一个匹配的元素并返回其索引;未找到则返回-1。

public int indexOf(int e) {for (int i = 0; i < size; i++) {if (array[i] == e) {return i;}}return -1;
}
③ 判断元素是否存在

复用indexOf()方法,若返回值 != -1则表示元素存在。

// 判断元素toFind是否在顺序表中
public boolean contains(int toFind) {return indexOf(toFind) != -1;
}
(3)修改元素(set(int pos, int e))

先校验索引合法性,再直接将array[pos]赋值为新元素(O(1)操作)。

// 将索引pos处的元素修改为e
public void set(int pos, int e) {checkPos(pos); // 校验索引合法性array[pos] = e; // 直接修改
}
(4)删除元素(remove(int e))

先校验空表,再通过indexOf()找到元素索引;若存在,将[pos+1, size-1]的元素向前移动1位,最后size--(有效元素个数-1)。

public void remove(int e) {try {checkEmpty(); int pos = indexOf(e);if (pos == -1) {System.out.println("删除失败!元素" + e + "不存在");return;}// 元素前移:从pos+1开始,到size-1结束,依次向前移1位for (int i = pos; i < size - 1; i++) {array[i] = array[i + 1];}size--; } catch (EmptyException e) {e.printStackTrace();}
}
(5)其他辅助功能
  • 清空顺序表:只需将size置为0(无需清空数组,后续添加会覆盖)
public void clear() {size = 0;
}
  • 打印顺序表所有元素
public void display() {try {checkEmpty(); // 校验是否空表for (int i = 0; i < size; i++) {System.out.print(array[i] + " ");}System.out.println(); // 换行} catch (EmptyException e) {e.printStackTrace();}
}
  • 获取有效元素个数
public int getSize() {return size;
}

三、ArrayList详解

3.1 ArrayList的简介

ArrayList是Java集合框架中的一个普通类,它实现了List接口,同时还实现了RandomAccessCloneableSerializable接口,这使得ArrayList具备了多种特性:

  1. 泛型实现ArrayList是以泛型方式实现的,在使用时必须先进行实例化,指定要存储的元素类型,这样可以保证类型安全,避免在运行时出现类型转换异常。
  2. 支持随机访问:由于实现了RandomAccess接口,ArrayList支持通过下标快速访问元素,这也是ArrayList相较于LinkedList的一个重要优势。
  3. 可克隆:实现Cloneable接口表明ArrayList是可以被克隆的,我们可以通过调用clone()方法来创建一个ArrayList对象的副本。
  4. 可序列化:实现Serializable接口意味着ArrayList支持序列化操作,能够将对象转换为字节流进行传输或持久化存储,在需要的时候再将字节流恢复为对象。
  5. 线程不安全:与Vector不同,ArrayList不是线程安全的。在单线程环境下可以放心使用,而在多线程环境中,如果需要保证线程安全,可以选择使用Vector或者CopyOnWriteArrayList
  6. 动态顺序表ArrayList的底层是一段连续的存储空间,并且能够根据元素的添加情况进行动态扩容,本质上是一个动态类型的顺序表。

3.2 ArrayList的构造方法

ArrayList提供了三种常用的构造方法,以满足不同的创建需求,具体如下表所示:

方法解释
ArrayList()无参构造方法,创建一个初始容量为空的ArrayList对象,在后续添加元素时会进行动态扩容
ArrayList(Collection<? extends E> c)利用其他实现了Collection接口的集合c来构建ArrayList,将集合c中的所有元素添加到新创建的ArrayList中
ArrayList(int initialCapacity)指定ArrayList的初始容量initialCapacity,创建一个具有指定初始容量的ArrayList对象,这种方式可以在已知大致元素数量的情况下,减少后续扩容的次数,提高性能

下面通过代码示例来展示ArrayList的创建方式:

public static void main(String[] args) {// 构造一个空的ArrayList,推荐写法,指定存储Integer类型元素List<Integer> list1 = new ArrayList<>();// 构造一个初始容量为10的ArrayListList<Integer> list2 = new ArrayList<>(10);list2.add(1);list2.add(2);list2.add(3);// list2.add("hello"); // 编译失败,因为List<Integer>已限定只能存储Integer类型元素// 利用已有的list2构造list3,构造好之后list3与list2中的元素一致ArrayList<Integer> list3 = new ArrayList<>(list2);// 不推荐的写法,省略了元素类型,这样list4可以存储任意类型的元素,使用时容易出现问题List list4 = new ArrayList();list4.add("111");list4.add(100);
}

3.3 ArrayList的常见操作

ArrayList的常用操作方法与List接口中定义的方法基本一致,下面通过代码示例来演示这些方法的具体使用:

public static void main(String[] args) {List<String> list = new ArrayList<>();// 向list中添加元素list.add("JavaSE");list.add("JavaWeb");list.add("JavaEE");list.add("JVM");list.add("测试课程");System.out.println(list); // 输出:[JavaSE, JavaWeb, JavaEE, JVM, 测试课程]// 获取list中有效元素的个数System.out.println(list.size()); // 输出:5// 获取和设置指定下标位置的元素,注意下标必须在[0, size)范围内System.out.println(list.get(1)); // 输出:JavaWeblist.set(1, "JavaWEB");System.out.println(list.get(1)); // 输出:JavaWEB// 在指定下标位置插入元素,该位置及后续元素会统一向后搬移一个位置list.add(1, "Java数据结构");System.out.println(list); // 输出:[JavaSE, Java数据结构, JavaWEB, JavaEE, JVM, 测试课程]// 删除指定元素,找到后删除,该元素之后的元素会统一向前搬移一个位置list.remove("JVM");System.out.println(list); // 输出:[JavaSE, Java数据结构, JavaWEB, JavaEE, 测试课程]// 删除指定下标位置的元素,注意下标不要超过有效元素个数,否则会抛出下标越界异常list.remove(list.size() - 1);System.out.println(list); // 输出:[JavaSE, Java数据结构, JavaWEB, JavaEE]// 检测list中是否包含指定元素,包含返回true,否则返回falseif (list.contains("测试课程")) {list.add("测试课程");}System.out.println(list); // 输出:[JavaSE, Java数据结构, JavaWEB, JavaEE](因为list中不包含"测试课程",所以未添加)// 查找指定元素第一次和最后一次出现的位置list.add("JavaSE");System.out.println(list.indexOf("JavaSE")); // 输出:0(从前往后找)System.out.println(list.lastIndexOf("JavaSE")); // 输出:4(从后往前找)// 截取list中[0, 4)之间的元素,构成一个新的SubList返回,注意新列表与原列表共用同一个存储数组List<String> ret = list.subList(0, 4);System.out.println(ret); // 输出:[JavaSE, Java数据结构, JavaWEB, JavaEE]// 清空list中的所有元素list.clear();System.out.println(list.size()); // 输出:0
}

注意:

  1. remove方法可以通过传入元素或下标删除,构成重载。如果顺序表中存放的是整形,要通过元素进行移除,就要传入对象而非整形。
    例如:移除元素1
list.remove(new Integer(1));
  1. subList()只是拿到了列表的下标,返回的列表与原列表共用同一个存储数组。因此对返回的列表修改,也会改变原列表。
//list1={1,2,3,4,5}
List<Integer> list2 = list.subList(1,3);
list2.set(0,99);
System.out.println(list1);//输出{1,99,3,4,5}

3.4 ArrayList的遍历方式

ArrayList提供了三种常用的遍历方式,分别是for循环+下标、foreach循环以及使用迭代器,下面通过代码示例进行展示:

public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
  • 方式一:使用下标+for循环遍历
    for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i) + " ");}System.out.println(); // 输出:1 2 3 4 5 
  • 方式二:借助foreach循环遍历
    for (Integer integer : list) {System.out.print(integer + " ");}System.out.println(); // 输出:1 2 3 4 5 
  • 方式三:使用迭代器遍历
    Iterator<Integer> it = list.listIterator();//获取迭代器while (it.hasNext()){ //如果有下一个元素就向后移动,并打印System.out.print(it.next() + " ");}System.out.println(); // 输出:1 2 3 4 5 
}

在实际开发中,ArrayList最常用的遍历方式是for循环+下标和foreach循环。迭代器是一种设计模式,它提供了一种统一的遍历集合元素的方式,在后续接触更多容器时,会对迭代器有更深入的了解。

3.5 ArrayList的扩容机制

ArrayList是一个动态类型的顺序表,这意味着在向其中添加元素的过程中,如果底层的存储空间不足,它会自动进行扩容。下面我们通过分析ArrayList的源码来深入了解其扩容机制。

首先来看ArrayList中的一些关键成员变量:

// 用于存储元素的数组
Object[] elementData; 
// 默认的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
// 默认的初始容量大小
private static final int DEFAULT_CAPACITY = 10; 

当我们调用add(E e)方法向ArrayList中添加元素时,会首先调用ensureCapacityInternal(size + 1)方法来确保底层数组有足够的空间存储新元素,具体代码如下:

public boolean add(E e) {ensureCapacityInternal(size + 1); // 确保数组容量足够,size为当前元素个数elementData[size++] = e; // 将新元素添加到数组中,并将元素个数加1return true;
}

ensureCapacityInternal方法又会调用ensureExplicitCapacity方法,而ensureExplicitCapacity方法会先判断是否需要扩容,如果需要则调用grow方法进行扩容,代码如下:

private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}private static int calculateCapacity(Object[] elementData, int minCapacity) {// 如果当前数组是默认的空数组,返回默认初始容量(10)和minCapacity中的较大值if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}private void ensureExplicitCapacity(int minCapacity) {modCount++; // 记录集合结构修改的次数// 如果所需的最小容量大于当前数组的长度,说明需要扩容if (minCapacity - elementData.length > 0)grow(minCapacity);
}

grow方法是ArrayList扩容的核心方法,它会根据当前数组的长度计算新的容量,并进行扩容操作,具体代码如下:

// 数组的最大容量,为Integer.MAX_VALUE - 8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;private void grow(int minCapacity) {// 获取当前数组的长度(旧容量)int oldCapacity = elementData.length;// 预计按照1.5倍的方式扩容(通过右移一位实现,相当于oldCapacity * 0.5)int newCapacity = oldCapacity + (oldCapacity >> 1);// 如果计算出的新容量小于所需的最小容量,就将新容量设置为所需的最小容量if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 如果新容量超过了数组的最大容量,需要重新计算新容量if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 调用Arrays.copyOf方法创建一个新的数组,并将原数组中的元素拷贝到新数组中,完成扩容elementData = Arrays.copyOf(elementData, newCapacity);
}private static int hugeCapacity(int minCapacity) {// 如果所需的最小容量小于0,说明发生了内存溢出,抛出OutOfMemoryError异常if (minCapacity < 0)throw new OutOfMemoryError();// 如果所需的最小容量大于数组的最大容量,返回Integer.MAX_VALUE,否则返回数组的最大容量return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

综合以上源码分析,我们可以将ArrayList的扩容机制总结为以下几个步骤:

  1. 检测扩容需求:在添加元素时,首先计算所需的最小容量,然后判断当前数组的容量是否能够满足该需求,如果不能则触发扩容操作。
  2. 计算新容量
    • 初步预估新容量为当前容量的1.5倍(通过oldCapacity + (oldCapacity >> 1)计算)。
    • 如果预估的新容量小于所需的最小容量,就将新容量设置为所需的最小容量。
    • 在确定最终新容量之前,还会检测新容量是否超过了数组的最大容量(Integer.MAX_VALUE - 8),如果超过则根据所需最小容量的大小,将新容量设置为Integer.MAX_VALUE或数组的最大容量。
  3. 执行扩容:调用Arrays.copyOf方法创建一个具有新容量的数组,并将原数组中的元素拷贝到新数组中,从而完成扩容操作。

3.6 ArrayList的问题与思考

虽然ArrayList在很多场景下都非常实用,但它也存在一些问题:

  1. 插入删除效率低:由于ArrayList底层使用连续的存储空间,当在任意位置插入或删除元素时,需要将该位置后续的元素整体往前或往后搬移,这会导致插入和删除操作的时间复杂度为O(N),在元素数量较多时,效率较低。
  2. 扩容消耗:当ArrayList需要扩容时,需要申请新的存储空间,将原数组中的元素拷贝到新数组中,然后释放旧的存储空间,这个过程会产生一定的系统开销。
  3. 空间浪费:ArrayList的扩容通常是按照当前容量的1.5倍进行的,这就可能导致一定的空间浪费。例如,当前容量为100,当元素填满后会扩容到200,如果之后只再插入5个元素,那么就会浪费95个元素的存储空间。

那么,如何解决这些问题呢?这就需要我们根据具体的业务场景选择合适的数据结构。例如,如果需要频繁进行插入和删除操作,可以考虑使用LinkedList,它通过链式存储结构,避免了元素的整体搬移,插入和删除操作的效率更高,但随机访问效率较低。在实际开发中,我们需要根据数据的操作特点,权衡各种数据结构的优缺点,选择最适合的方案。

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

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

相关文章

K8S里的“豌豆荚”:Pod

1. 为什么要有podPod 这个词原意是“豌豆荚”&#xff0c;后来又延伸出“舱室”“太空舱”等含义&#xff0c;你可以看一下这张图片&#xff0c;形 象地来说 Pod 就是包含了很多组件、成员的一种结构。之前的容器技术让进程在一个“沙盒”环境里运行&#xff0c;具有良好的隔离…

vue3 基本教程-运行一个最小demo

Vue 3 基本教程 - 运行一个最小 Demo 1. 创建项目 使用 Vue 官方脚手架工具创建一个新项目&#xff1a; # 安装 Vue CLI (如果尚未安装) npm install -g vue/cli# 创建一个新项目 vue create vue3-demo# 选择 Vue 3 预设 # 使用方向键选择 "Default (Vue 3)" 然后按 …

大数据新视界 -- Hive 集群搭建与配置的最佳实践(2 - 16 - 13)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的博客,正是这样一个温暖美好的所在。在这里,你们不仅能够收获既富有趣味又极为实…

C/C++ 转 Java 的数据结构初阶对比指南

一、先遣了解和回顾1、预览快速对比表格数据结构​​​​C/C 实现​​​​Java 实现​​​​关键区别​​​​数组​​int arr[5];int[] arr new int[5];语法类似&#xff0c;Java 数组是对象​​动态数组​​vector<int> v;ArrayList<Integer> list new ArrayLi…

长连接和短连接

在网络通信中&#xff0c;长连接&#xff08;Long Connection&#xff09;和短连接&#xff08;Short Connection&#xff09;是两种核心的连接管理策略&#xff0c;其区别主要体现在连接生命周期、资源占用和适用场景上。以下是两者的详细解析&#xff1a;一、核心概念对比特性…

Java:使用spring-cloud-gateway的应用报DnsNameResolverTimeoutException原因和解决方法

使用spring-cloud-gateway时&#xff0c;有时会报DnsNameResolverTimeoutException异常。堆栈信息类似&#xff1a;Caused by: java.net.UnknownHostException: Failed to resolve cloudconnector.linkup-sage.comat io.netty.resolver.dns.DnsResolveContext.finishResolve(Dn…

SpringCloud概述

目录 一、概念 1.1 微服务架构 1.2 SpringCloud概念 1.3 核心价值 1.4 能力边界 1.5 微服务总体架构图 二、生态圈 2.1 不同生态圈组件对比 2.2 组件介绍 2.2.1 服务发现与注册 2.2.2 配置管理 2.2.3 API网关 2.2.4 容错与熔断 2.2.5 客户端负载均衡 2.2.6 服务…

光伏电站环境监测仪—专为光伏电站设计的气象监测设备

光伏电站环境监测仪是专为光伏电站设计的气象监测设备&#xff0c;通过实时采集关键环境参数&#xff0c;为光伏系统的发电效率评估、运维决策和安全预警提供数据支撑。监测参数太阳辐射采用高精度总辐射表&#xff0c;测量水平面总辐射和倾斜面辐射&#xff0c;精度达 2% 以内…

Node.js ≥ 18 安装教程

Windows 安装 下载安装包&#xff1a;访问 Node.js官网&#xff0c;下载最新的 LTS 版本&#xff08;确保版本 ≥ 18&#xff09;运行安装程序&#xff1a;双击下载的安装文件&#xff0c;按照向导完成安装验证安装&#xff1a;打开命令提示符或PowerShell&#xff0c;输入以下…

电脑 hdmi 没有声音问题解决

问题现象&#xff1a;电脑耳机声音正常输出&#xff0c;使用hdmi连接电视后&#xff0c;没有声音输出。&#xff08;正常会通过hdmi 在电视上播放视频和声音&#xff09;解决方案:出现该情况很可能原因是 显卡的驱动不对。网上找了各种方法都没有解决&#xff0c;最后系统升级后…

学习日记-XML-day55-9.14

1.xml基本介绍知识点核心内容重点XML定义可扩展标记语言&#xff0c;用于数据存储和传输与HTML的区别&#xff08;HTML用于展示&#xff0c;XML用于结构化数据&#xff09;XML用途1. 配置文件&#xff08;Spring的beans.xml、Tomcat的server.xml&#xff09;;2. 数据交换&#…

【系统架构设计(27)】信息安全技术集成

文章目录一、本文知识覆盖范围二、信息安全基础要素详解1、机密性保障技术2、完整性验证技术3、可用性保障技术4、可控性管理技术5、可审查性追溯技术三、网络安全威胁与防护策略1、非授权访问防护2、拒绝服务攻击防护3、恶意软件传播防护四、加密技术体系与应用1、对称加密技术…

什么是 SaaS 安全?

什么是 SaaS 安全&#xff1f; SaaS 安全专注于保护云中的数据、应用程序和用户身份。它旨在应对基于云的软件所面临的挑战&#xff0c;以确保信息的安全性和可用性。SaaS 安全致力于降低未授权访问、数据泄露等风险&#xff0c;同时增强 SaaS 应用程序的安全性。 SaaS 安全不仅…

mysql和postgresql如何选择

h5打开以查看 简单来说&#xff1a; MySQL&#xff1a;更像是一个“快速、可靠的工匠”&#xff0c;注重速度、简单和稳定性&#xff0c;尤其在读操作密集的Web应用中是经典选择。 PostgreSQL&#xff1a;更像是一个“功能强大的学者”&#xff0c;追求功能的完备性、标准的符…

Redis最佳实践——安全与稳定性保障之数据持久化详解

Redis 在电商应用的安全与稳定性保障之数据持久化全面详解一、持久化机制深度解析 1. 持久化策略矩阵策略触发方式数据完整性恢复速度适用场景RDB定时快照分钟级快容灾备份/快速恢复AOF实时追加日志秒级慢金融交易/订单关键操作混合模式RDBAOF同时启用秒级中等高安全要求场景无…

Data Augmentation数据增强

目录 数据增强是什么 为什么数据增强 数组增强分类 有监督数据增强 无监督数据增强 数据增强是什么 数据增强又称数据扩增&#xff0c;是一种通过应用合理且随机的变换&#xff08;例如图像位移、旋转&#xff09;来增加训练集多样性的技术。让有限的数据产生等价于更多数…

OpenCV:特征提取

目录 一、特征提取核心概念&#xff1a;什么是图像特征&#xff1f; 二、实战 1&#xff1a;Harris 角点检测 1.1 角点的物理意义 1.2 Harris 算法原理 1.3 OpenCV 实战代码与解析 1.4 结果分析 三、实战 2&#xff1a;SIFT 特征提取 3.1 SIFT 算法核心优势 3.2 SIFT…

MySQL的查找加速器——索引

文章目录 目录 前言 一、基础概念&#xff1a;什么是 MySQL 索引&#xff1f; 二、底层数据结构&#xff1a;为什么 InnoDB 偏爱 B 树&#xff1f; B 树的结构特点&#xff08;以短链接表short_link的short_code索引为例&#xff09;&#xff1a; B 树的优势&#xff1a…

【Vue2手录11】Vue脚手架(@vue_cli)详解(环境搭建+项目开发示例)

一、前言&#xff1a;为什么需要 Vue 脚手架&#xff1f; 手动搭建 Vue 项目存在诸多痛点&#xff08;原笔记提及&#xff09;&#xff1a; 依赖管理复杂&#xff1a;需手动下载 Vue、Babel、Webpack 等工具&#xff0c;处理版本兼容性。配置繁琐&#xff1a;Webpack 配置、E…

自签发、CA机构签发、SSH、SCP、RSYNC,SUDO详解

一、为什么&#xff1f; 1. 自建CA为什么比Lets Encrypt强&#xff1f; 不能把CA放公网&#xff01;Lets Encrypt是给公网服务用的&#xff08;比如10.0.0.30的Web服务&#xff09;&#xff0c;但内网服务&#xff08;比如OpenVPN&#xff09;必须用自签CA。 CA私钥必须物理隔…