ArrayList 万字长文解析:使用、优化、源码分析

news2024/11/24 19:41:29

文章目录

  • ArrayList 万字长文解析:使用、优化、源码分析
    • 前言
    • ArrayList 简介
    • ArrayList 的基本使用方法
    • ArrayList 性能优化
    • ArrayList 的源码分析
      • 内部结构
      • 构造方法解析
      • 扩容机制
      • System.arraycop与 Arrays.copyof 实现方式 与 使用场景
      • 迭代器
    • JDK 8版本 ArrayList bug 示例
    • ArrayList 使用注意事项代码演示
    • ArrayList 与其他集合类的比较
    • 结尾

ArrayList 万字长文解析:使用、优化、源码分析

ArrayList 万字长文解析 使用、优化、源码分析

前言

  大家好,这里是 Rocky 编程日记 ,一位喜欢后端架构及中间件源码的CSDN博主,该篇文章主要是对 Java 集合框架体系下 ArrayList的一个解析 。

  梳理出来也是想和大伙探讨一下这块内容,同时也供大家学习交流,如若笔记中有不对的地方,那一定是我的理解还不够,希望你大胆的在评论区指出来噢~。

  同时,如果对于该笔记存在很多疑惑,也欢迎和我交流讨论,最后也感谢您的阅读,嘿嘿,期待你的点赞,关注,收藏噢~。

  前人述备矣,我只是知识的搬运工,ArrayList 文中代码示例皆在开源代码仓库中,源代码仓库地址为: https://gitee.com/Rocky-BCRJ/java-diary.git。欢迎star~。

  同时,本文只是对 ArrayList 核心方法的一些讲解,如果有一些相关方法未讲解到的,欢迎评论区留言讨论,后续我也会把 JDK 注释的开源代码同步出来,敬请期待~。

ArrayList 简介

  ArrayList 是一种动态数组,可以在运行时自动扩展容量。它可以存储任何类型的对象,并提供高效的随机访问和快速的插入/删除操作。ArrayList 有以下优点:

  1. 高效的随机访问:ArrayList 内部使用数组实现,因此可以通过下标快速访问任意元素。
  2. 动态扩容:ArrayList 可以在运行时自动扩展容量,因此可以存储任意数量的元素。
  3. 可以存储任何类型的对象:ArrayList 可以存储任何类型的对象,包括基本类型和自定义类型。
  4. 支持插入/删除操作:ArrayList 内部使用数组实现,因此可以通过移动元素来实现插入和删除操作。

然而,ArrayList 也有一些缺点:

  1. 插入和删除操作可能会导致数组元素的移动,因此在大量插入和删除操作时,ArrayList 的性能可能会受到影响。
  2. ArrayList 内部使用数组实现,因此在插入和删除操作时,可能需要重新分配数组空间,这可能会导致性能下降。
  3. ArrayList 只能存储对象,不能存储基本类型,因此需要将基本类型包装成对象,这可能会导致一些额外的开销。

ArrayList 的基本使用方法

该段介绍 ArrayList 的基本操作,如添加、删除、遍历等。

package cn.rocky.other;

import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListDemo {

    public static void main(String[] args) {
        // 创建 ArrayList 对象
        ArrayList<String> list = new ArrayList<>();

        // 添加元素
        list.add("Java");
        list.add("Python");
        list.add("C++");

        // 获取元素
        System.out.println("第二个元素是:" + list.get(1));

        // 修改元素
        list.set(1, "JavaScript");
        System.out.println("修改后的第二个元素是:" + list.get(1));

        // 删除元素
        list.remove(0);
        System.out.println("删除后的第一个元素是:" + list.get(0));

        System.out.println("for遍历元素:");
        // for
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }

        System.out.println();
        System.out.println("for each 遍历元素:");
        // for each 遍历元素
        for (String s : list) {
            System.out.print(s + " ");
        }

        System.out.println();
        System.out.println("迭代器遍历元素:");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.print(element + " ");
        }
    }
}

ArrayList 性能优化

当使用 ArrayList 时,可以考虑以下性能优化技巧:

  1. 指定 ArrayList 的初始容量:在创建 ArrayList 时,可以指定其初始容量,这可以避免在添加元素时频繁地扩展数组。例如,如果预计 ArrayList 中将包含 100 个元素,可以使用以下代码创建 ArrayList:
ArrayList<String> list = new ArrayList<>(100);
  1. 使用 Iterator 迭代器遍历 ArrayList:使用 Iterator 迭代器遍历 ArrayList 可以避免使用 for 循环时频繁地调用 get() 方法。例如:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    // do something with element
}
  1. 使用 for-each 循环遍历 ArrayList:使用 for-each 循环遍历 ArrayList 可以使代码更加简洁,也可以避免使用 for 循环时频繁地调用 get() 方法。例如:
for (String element : list) {
    // do something with element
}
  1. 使用 toArray() 方法将 ArrayList 转换为数组:如果需要频繁地访问 ArrayList 中的元素,可以使用 toArray() 方法将 ArrayList 转换为数组,以提高访问速度。例如:
String[] array = list.toArray(new String[0]);
  1. 使用 subList() 方法获取子列表:如果需要对 ArrayList 中的部分元素进行操作,可以使用 subList() 方法获取子列表,以避免对整个 ArrayList 进行操作。例如:
List<String> subList = list.subList(0, 10);
// do something with subList

  这些技巧可以帮助提高 ArrayList 的性能,但需要根据具体情况进行选择。在实际使用中,还需要考虑其他因素,比如数据量、操作频率等。

ArrayList 的源码分析

  在这一部分中,我们将深入分析 ArrayList 的源码,以深入理解其实现原理和性能特点。我们将会讨论 ArrayList 的内部结构、扩容机制、迭代器实现等重要内容。

内部结构

  1. 数组元素:ArrayList 内部使用 Object 类型的数组来存储元素,可以通过数组下标来访问元素。
  2. 元素数量:ArrayList 中的元素数量,也就是当前数组中实际存储的元素数量。
  3. 容量大小:ArrayList 中数组的容量大小,也就是数组能够存储的最大元素数量。当元素数量超过容量大小时,ArrayList 会自动扩容。
  4. 修改次数:ArrayList 中记录修改次数的变量,用于在迭代器遍历时检测是否有其他线程对 ArrayList 进行修改。
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 默认初始化容量 10
		private static final int DEFAULT_CAPACITY = 10;
	  // 共享的空数组对象
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 共享的空数组对象 
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 思考? 这里为啥要加 transient。
    // 存储 ArrayList 中元素的数组
    transient Object[] elementData; 
		// 实际存储的元素数量
    private int size;
    // 父类的元素
    // modCount 变量表示 ArrayList 的结构修改次数,
    // 用于在迭代器遍历时检测是否发生了结构性修改,如果发生了则抛出 ConcurrentModificationException 异常
    protected transient int modCount = 0;
}  

构造方法解析

		// 默认构造函数
		public ArrayList() {
        // 将 elementData 成员变量初始化为一个空数组,表示 ArrayList 的初始容量为 0。
        // 当第一个元素被添加到 ArrayList 中时,elementData 数组会被扩容成默认容量 10。
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
		public ArrayList(int initialCapacity) {
        // 初始化容量大于 0 时,创建 Object 数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        }
         // 初始化容量等于 0 时,使用 EMPTY_ELEMENTDATA 对象
        else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } 
        // 初始化容量小于 0 时,抛出 IllegalArgumentException 异常
        else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
		public ArrayList(Collection<? extends E> c) {
      	// 将 集合 c 转换成 Object 数组。 思考?这里能用 elementData去接么 element = c.toArray(); 
        Object[] a = c.toArray();
      	// 数组包含 元素时(length != 0)
        if ((size = a.length) != 0) {
            // 类型为 ArrayList,直接赋值
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } 
            // 集合元素不是 Object[] 类型,则会创建 新的 Object[] 数组,并将 元素赋值到其中,最后赋值给 elementData。 
            else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } 
        // 如果数组中不包含 元素时(length == 0),则使用 EMPTY_ELEMENTDATA 。
        else {
            elementData = EMPTY_ELEMENTDATA;
        }
    }

扩容机制

 		public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 加元素
        elementData[size++] = e;
        return true;
    }
		private void ensureCapacityInternal(int minCapacity) {
	// 计算 calculate 得到的容量大小 ,判断是否需要进行扩容 
      ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    // 计算容量 1.当前数组 2.minCapacity = size + 1
		private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 如果当前数组等于{}, 返回默认初始化大小 10;
      	if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
      	// 返回 size + 1;
        return minCapacity;
    }
		private void ensureExplicitCapacity(int minCapacity) {
        // 修改次数 +1
      	modCount++;

        // overflow-conscious code
      	// 需要的minCapacity容量大于数组元素长度,则需要进行扩容
        if (minCapacity - elementData.length > 0)
          	// 扩容
            grow(minCapacity);
    }
	 // 扩容方法	
	 private void grow(int minCapacity) {
        // overflow-conscious code 
     		// 意思是指在编写代码时考虑数据溢出的情况,避免因为数据溢出而导致程序出错或崩溃。
     		// 例如,在进行数值计算时,如果结果超出了数据类型的范围,就会发生数据溢出,此时就需要使用 overflow-conscious code 来避免这种情况。
     	  // 旧数组容量大小,也即当前数组容量
        int oldCapacity = elementData.length;
     	  // 计算新数组容量,新数组容量 = 旧数组容量 + 1/2旧数组容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果新数组容量小于最小容量,则将最小容量作为新的数组容量
     		if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 如果新的数组容量超过了数组最大容量(Integer.Max - 8),则调用 hugeCapacity 方法进入扩容
      	if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
		// 计算扩容的最大容量, 在 Java 中,数组的最大长度是 Integer.MAX_VALUE, hugeCapacity 方法就能保证在扩容时,不会超过数组的最大长度限制。
		private static int hugeCapacity(int minCapacity) {
        // 传入的 minCapacity 小于 0,抛出 oom
      	if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        // 如果 minCapacity 大于 MAX_ARRAY_SIZE,则返回 Integer.MAX_VALUE,否则返回 MAX_ARRAY_SIZE。
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

System.arraycop与 Arrays.copyof 实现方式 与 使用场景

System.arraycopy()Arrays.copyOf() 都可以用来复制一个数组,但是它们的实现方式和使用场景略有不同。

System.arraycopy() 是一个本地方法,它可以快速地将一个数组的一部分复制到另一个数组中的指定位置。它的语法如下:

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

其中,src 是源数组,srcPos 是源数组中要复制的起始位置,dest 是目标数组,destPos 是目标数组中要复制到的起始位置,length 是要复制的元素数量。

Arrays.copyOf() 是一个静态方法,它可以将一个数组复制到一个新的数组中,并且可以指定新数组的长度。它的语法如下:

public static <T> T[] copyOf(T[] original, int newLength);

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

其中,original 是要复制的原始数组,newLength 是新数组的长度。

总的来说,System.arraycopy() 适用于需要高效复制数组的场景,而 Arrays.copyOf() 则更适用于需要创建一个新数组并且长度不确定的场景。另外,Arrays.copyOf() 还支持将原始数组的元素类型转换为另一种类型,而 System.arraycopy() 不支持。

迭代器

  1. 迭代器的作用:迭代器是一种用于遍历集合类中元素的工具,它可以让我们轻松地遍历 ArrayList 中的元素,而不需要使用传统的 for 循环或者 foreach 循环。

  2. 迭代器的使用方法:通过调用 ArrayList 的 iterator() 方法可以获取一个迭代器对象,然后可以使用 hasNext() 方法和 next() 方法来遍历 ArrayList 中的元素。需要注意的是,在使用迭代器遍历元素时,不能直接使用 ArrayList 的 get() 方法获取元素。

  3. 迭代器的优点:相比传统的 for 循环或者 foreach 循环,使用迭代器遍历集合类中的元素有以下几个优点:

    • 可以在遍历过程中删除元素,而不会出现 ConcurrentModificationException 异常。
    • 可以遍历所有实现了 Iterable 接口的集合类,而不需要关心具体的集合类型。
    • 可以避免使用索引来访问元素,从而避免出现数组越界等异常。
  4. 迭代器的缺点:使用迭代器遍历集合类中的元素也有一些缺点:

    • 不能在遍历过程中修改元素,否则会出现 ConcurrentModificationException 异常。
    • 遍历过程中不能向集合中添加新的元素,否则会导致遍历不完整。

JDK 8版本 ArrayList bug 示例

public class TestList {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3);
        Object[] array = list.toArray();
        // JDK8 返回 Integer[] 数组,JDK9+ 返回 Object[] 数组。
        System.out.println("class name : " + array.getClass().getSimpleName());
        // JDK 8 和 JDK9+ 表现不同, 前者会报 ArrayStoreException 异常, 后者不会 
        array[0] = new Object();
    }
}

ArrayList 使用注意事项代码演示

  当使用 subList() 方法获取 ArrayList 的子列表时,返回的是一个新的列表对象,但是它与原来的列表对象共享同一个数据存储区域,也就是说,对子列表的修改会影响到原来的列表对象。如果在对子列表进行修改的同时,原来的列表对象也被修改了,就会导致 ConcurrentModificationException 异常的抛出。

package cn.rocky.other;

import java.util.ArrayList;
import java.util.List;

public class TestList04 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        List<String> subList = list.subList(0, 2);

        list.add("D"); // 修改原列表

        subList.clear(); // 修改子列表

        // 此时会抛出 ConcurrentModificationException 异常
    }
}

结果如下:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1241)
	at java.util.ArrayList$SubList.size(ArrayList.java:1050)
	at java.util.AbstractList.clear(AbstractList.java:234)
	at cn.rocky.other.TestList04.main(TestList04.java:18)

  多线程环境下操作 ArrayList 也会出现一些其妙的问题,代码如下:

package cn.rocky.other;

import java.util.ArrayList;

public class TestList05 {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 500; i++) {
                list.remove(i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 500; i++) {
                list.remove(i);
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(list.size());
    }
}

结果如下:

Exception in thread "Thread-1" Exception in thread "Thread-0" java.lang.IndexOutOfBoundsException: Index: 459, Size: 276
	at java.util.ArrayList.rangeCheck(ArrayList.java:659)
	at java.util.ArrayList.remove(ArrayList.java:498)
	at cn.rocky.other.TestList05.lambda$main$1(TestList05.java:20)
	at java.lang.Thread.run(Thread.java:750)
java.lang.IndexOutOfBoundsException: Index: 276, Size: 276

ArrayList 与其他集合类的比较

  1. ArrayList:底层是基于数组实现的,因此支持随机访问和快速的插入、删除操作,但是在插入、删除元素时可能需要移动其他元素,因此效率不如 LinkedList。ArrayList 还有一个优点是它的空间利用率高,因为它的容量是动态扩展的,而且可以通过设置初始容量来避免频繁扩容。
  2. LinkedList:底层是基于链表实现的,因此支持快速的插入、删除操作,但是随机访问效率较低,因为需要从头开始遍历链表。LinkedList 还有一个优点是它支持高效的队列和栈操作,因为可以在链表的头部和尾部进行插入、删除操作。
  3. Vector:与 ArrayList 类似,底层也是基于数组实现的,但是它是线程安全的,因此在多线程环境下使用更加安全,但是效率较低。Vector 还有一个缺点是它的扩容机制是翻倍扩容,因此在扩容时可能会浪费一些空间。

结尾

  感谢您阅读本文,如果您有任何问题或建议,请在评论区留言,我将尽快回复您。如果你也有关于 ArrayList 使用的心得体会,欢迎评论区留言讨论。如果您觉得本文对您有所帮助,请点赞,评论,收藏,如果可以的话请分享给更多的人,让更多的人受益。

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

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

相关文章

如何安装pycharm的包

PyCharm是JetBrains公司推出的一款Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;可以提供高效的Python代码编写、调试和测试。以下是一些PyCharm的主要功能&#xff1a; 代码智能提示和自动补全功能&#xff1b;支持调试和测试Python代码&#xff1b;完整的Pyth…

如何正确地处理职场中的失误

如何正确地处理职场中的失误 在职场上&#xff0c;我们都会遇到各种各样的问题和挑战。面对这些问题时&#xff0c;我们有时难免会犯错。但正如一句古老的谚语所说&#xff1a;“失败是成功之母。”我们应该从失误中吸取教训&#xff0c;不断成长。本文将探讨如何正确地处理职…

【云原生-K8s-2】kubeadm搭建k8s高可用集群(三主两从一VIP)完整教程

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录 Kubernetes高可用集群部署准备工作&a…

chatgpt赋能python:Python就业前景展望:一门热门语言的发展趋势分析

Python就业前景展望&#xff1a;一门热门语言的发展趋势分析 Python是一种高级、解释型编程语言&#xff0c;已经成为最受欢迎的语言之一。Python的良好生态系统、易用性和强大的库和框架支持使得许多公司和组织使用Python来构建各种类型的应用程序、工具和系统。Python广泛应…

实施了这么多WMS系统之后,总结10点感悟分享

WMS系统是现代物流行业中不可或缺的一环&#xff0c;通过WMS系统的实施&#xff0c;企业可以更好地管理内部仓储、运输和配送工作&#xff0c;从而提高工作效率和减少成本。在实施WMS系统之后&#xff0c;我们对于这一过程有了更深入的了解和感悟。 1.要注意数据准确性&#xf…

ESP8266 SDK开发阶段无线WIFI本地烧录升级程序 -- FOTA

【本文发布于https://blog.csdn.net/Stack_/article/details/130448713&#xff0c;未经允许不得转载&#xff0c;转载须注明出处】 前言 因为正在DIY一个WiFi计量插座&#xff0c;采用非隔离的方案&#xff0c;烧录时要拔掉220V插头&#xff0c;测试时要拔掉USB线&#xff0c;…

webpack指定输出资源的路径和名称

如图&#xff0c;在前面的章节我们打包后的文件默认都输出到了dist目录下&#xff0c;无论是图片、还是js都在同一级别目录&#xff0c;这里目前处理的资源比较少&#xff0c;如果资源一多&#xff0c;所有的资源都在同一级目录&#xff0c;看起来很费劲。 那么这节就介绍一下…

【LeetCode】74. 搜索二维矩阵

74. 搜索二维矩阵&#xff08;中等&#xff09; 方法一&#xff1a;二分查找 思路 总体思路 由于二维矩阵固定列的「从上到下」或者固定行的「从左到右」都是升序的 因此我们可以使用两次二分来定位到目标位置。 第一次二分&#xff1a; 从第 0 列中的「所有行」开始找&#x…

Threejs,InstancedMesh变换操作

Threejs,InstancedMesh 在项目中加载一个道路的模型树,结果加载出来是水平的 期望是: 仔细分析: 打印模型元素,模型是两个交叉的InstancedMesh ,每个InstancedMesh 里面有566重复的mesh,形成566棵树。那么现在的期望就变成这两个交叉的InstancedMesh 各自旋转下角度 c…

高考作文题押中了?其实是一种趋势:探索、好奇、故事、技术、时间…

探索、好奇、故事、技术、时间 不知不觉&#xff0c;距离高考已经过去了23年……今年的高考题&#xff0c;关键词非常有时代感&#xff0c;不禁让人联想背后的趋势是什么&#xff1f; 趋势是无法抵挡的浪潮&#xff0c;一旦你没有跟上&#xff0c;这个时代的叙事就没有你的一份…

卡尔曼滤波与组合导航原理(二)卡尔曼滤波方程的推导

文章目录 三、卡尔曼滤波1、随机系统状态空间模型2、状态预测3、状态量测4、增益矩阵K与状态估计5、Kalman滤波公式汇总6、Kalman滤波流程图1.划分为左右两部分&#xff08;一阶矩和二阶矩&#xff09;2.划分为上下两部分&#xff08;时间更新、量测更新&#xff09; 7、Kalman…

关于如何清理过多索引的思考

今天同事提了一个问题&#xff0c;还是值得思考的&#xff0c;某个作为数据分发的MySQL库&#xff0c;有时候需要在不同的环境中同步创建数据库&#xff0c;但受工具限制&#xff0c;只能做数据同步&#xff0c;索引这些对象则需要单独创建&#xff0c;该数据库的索引太多&…

在 Transformers 中使用约束波束搜索引导文本生成

引言 本文假设读者已经熟悉文本生成领域波束搜索相关的背景知识&#xff0c;具体可参见博文 如何生成文本: 通过 Transformers 用不同的解码方法生成文本。 与普通的波束搜索不同&#xff0c;约束 波束搜索允许我们控制所生成的文本。这很有用&#xff0c;因为有时我们确切地知…

学习笔记之MySQL索引

1、引言 索引是数据库用来提高性能最常用的工具&#xff0c;一般索引本身也很大&#xff0c;不可能全部存于内存中&#xff0c;因此所以往往以文件形式存于磁盘上。 左表是数据表&#xff0c;共两列七条数据。为了加快Col2的查找&#xff0c;可以维护一个右表所示的二叉查找树…

图论与算法(7)最短路径问题

1.最短路径问题 1.1 带权图的最短路径 最短路径问题是指在一个加权图中寻找两个顶点之间的最短路径&#xff0c;其中路径的长度由边的权重确定。 常见的最短路径算法包括&#xff1a; Dijkstra算法&#xff1a;适用于解决单源最短路径问题&#xff0c;即从一个固定的起点到图…

meethigher-阿里邮箱POP3/SMTP服务

最近发现一个问题&#xff0c;小伙伴给我发的邮件&#xff0c;收和回都不及时。于是我现在将所有的邮箱&#xff0c;通过POP3/SMTP协议整合到了一起。再配合小米手环&#xff0c;就能做到邮件无遗漏。 一、邮箱常用协议 邮箱中常用三类协议 POP3 Post Office Protocol versi…

chatgpt赋能python:Python就业学历要求

Python 就业学历要求 Python 是一门广泛应用于数据科学、人工智能、Web 开发和自动化等领域的编程语言&#xff0c;正在迅速成为行业内最受欢迎的语言之一。如果你想进入这些领域从事相关职业&#xff0c;那么 Python 编程技能将是你的一个优势。但是&#xff0c;Python 就业所…

基于SSM+JSP的毕业生就业信息管理系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

软考A计划-系统架构师-官方考试指定教程-(3/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…