当前位置: 首页 > news >正文

ArrayList源码分析

ArrayList源码分析

  • 目标:
  • 一、 ArrayList的简介
  • 二、ArrayList原理分析
    • 2.1 ArrayList的数据结构源码分析
    • 2.2 ArrayList默认容量&最大容量
    • 2.3 为什么ArrayList查询快,增删慢?
    • 2.4 ArrayList初始化容量
      • 1、创建ArrayList对象分析:无参数
      • 2、创建ArrayList对象分析:带有初始化容量构造方法
    • 2.5 ArrayList扩容原理
  • 三、ArrayList线程安全问题及解决方案
    • 3.1 错误复现
    • 3.2 导致ArrayList线程不安全的源码分析
    • 3.3 解决方案
  • 四、ArrayList的Fail-Fast机制深入理解

目标:

  • 理解ArrayList的底层数据结构
  • 深入掌握ArrayList查询快,增删慢的原因
  • 掌握ArrayList的扩容机制
  • 掌握ArrayList初始化容量过程
  • 掌握ArrayList出现线程安全问题原因及解决方案
  • 掌握ArrayList的Fail-Fast机制

一、 ArrayList的简介

在这里插入图片描述

ArrayList集合是CollectionList接口的实现类。底层的数据结构是数组。数据结构特点 : 增删慢,查询快。线程不安全的集合!
许多程序员开发的时候,使用集合基本上无脑选取ArrayList!不建议这种用法。
ArrayList的特点:

  • 单列集合 : 对应与Map集合来说【双列集合】
  • 有序性 : 存入的元素和取出的元素是顺序是一样的
  • 元素可以重复 : 可以存入两个相同的元素
  • 含带索引的方法 : 数组与生俱来含有索引【下角标】

二、ArrayList原理分析

2.1 ArrayList的数据结构源码分析

//空的对象数组
private static final Object[] EMPTY_ELEMENTDATA = {}; 
//默认容量空对象数组,通过空的构造参数生成ArrayList对象实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 
//ArrayList对象的实际对象数组!
transient Object[] elementData; // non-private to simplify nested class access
//1、为什么是Object类型呢?利用面向对象的多态特性,当前ArrayList的可以存储任意引用数据类型。
//2、ArrayList有一个问题,不能存储基本数据类型!就是数组的类型是Object类型

2.2 ArrayList默认容量&最大容量

//默认的初始化容量是10
private static final int DEFAULT_CAPACITY = 10; 
//最大容量    : 2^31 - 1 - 8 = 21 4748 3639【21亿】
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

为什么最大容量要-8呢?
目的是为了存储ArrayList集合的基本信息,比如list集合的最大容量!
在这里插入图片描述

2.3 为什么ArrayList查询快,增删慢?

ArrayList的底层数据结构就是一个Object数组一个可变的数组,对于其的所有操作都是通过数组来实 现的。

  • 数组是一种,查询快、增删慢!
  • 查询数据是通过索引定位,查询任意数据耗时均相同。查询效率贼高!
  • 删除数据时,要将原始数据删除,同时后面的每个数据迁移。删除效率就比较低
  • 新增数据,在添加数组的位置加入数组,同时在数组后面位置后移以为!添加效率极低!

在这里插入图片描述

2.4 ArrayList初始化容量

ArrayList底层是数组,动态数组!

  • 底层是Object对象数组,数组存储的数据类型是Object,数组名字为elementData。
 transient Object[] elementData; // non-private to simplify nested class access

1、创建ArrayList对象分析:无参数

创建ArrayList的之后,ArrayList容量是多少呢?回答10是错误的!回答0是正确【限定条件,在
JDK1.8中】
如何 初始化 动态数组的容量?10个

构造方法

/*** Constructs an empty list with an initial capacity of ten. */
//空参构造时,创建一个空数组 
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 
}
//空数组!
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

在执行add()方法的时候初始化!【懒加载】
判断当前数组的容量是否有存储空间,如果没有初始化一个10的容量。

//向数组中,添加一个元素 
public boolean add(E e) {//确保有容量,如果第一次添加,会初始化一个容量为10的list //size当前集合元素的个数,随着添加的元素递增ensureCapacityInternal(size + 1);  // Increments modCount!! //添加元素elementData[size++] = e; return true;
}
//ensureCapacityInternal确保有容量,如果第一次添加,会初始化一个容量为10的list 
private void ensureCapacityInternal(int minCapacity) {//两个方法ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); 
}
// calculateCapacity(elementData, minCapacity) 拿着当前ArrayList的数组,与当前数组中的元素个数。计算容量
private static int calculateCapacity(Object[] elementData, int minCapacity) 
{//ArrayList的数组与默认的数组进行比较。 //{}  == {}if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {/true //DEFAULT_CAPACITY = 10//minCapacity 1 //1和10比谁大 10return Math.max(DEFAULT_CAPACITY, minCapacity);//计算之后,返回的初始化容量是10}return minCapacity; 
}
// ensureExplicitCapacity() 确保不会超过数组的真实容量 
private void ensureExplicitCapacity(int minCapacity) { //minCapacity 当前计算后容量    10modCount++;//对当前数组操作计数器 // overflow-conscious code//最小的容量: 10 - 当前数组的容量{} 0if (minCapacity - elementData.length > 0) grow(minCapacity);//做了扩容
}

2、创建ArrayList对象分析:带有初始化容量构造方法

//创建ArrayList集合,并且设置固定的集合容量 
public ArrayList(int initialCapacity) { //initialCapacity 手动设置的初始化容量if (initialCapacity > 0) {//判断容量是否大于0,如果大于0 //创建一个对象数组位指定容量大小,并且交给ArrayList对象 this.elementData = new Object[initialCapacity]; //如果设置的容量为0,设置默认数组} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;//默认的元素数据数组{} } else {//如果不是0,也不是大于0的数,会抛出非法参数异常!throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);} 
}

注意 : 使用ArrayList的集合,建议如果知道集合的大小,最好提前设置。提示集合的使用效率!

2.5 ArrayList扩容原理

add方法先要确保数组的容量足够,防止数组已经填满还往里面添加数据造成数组越界:

  1. 如果数组空间足够,直接将数据添加到数组中
  2. 如果数组空间不够了,则进行扩容。扩容1.5倍扩容。
  3. 扩容 : 原始数组copy新数组中,同时向新数组后面加入数据

注意 : new的ArrayList的对象没有容量的,在第一次添加的add,会进行第一次扩容。0 -> 10!

//grow扩容数组
private void grow(int minCapacity) {//minCapacity 当前数组的最小容量,存储了多少个元素 // overflow-conscious code//获取当前存储数据数组的长度int oldCapacity = elementData.length;//新的容量 = 旧的容量 + 扩容的容量【旧容量/2 = 0.5旧容量】 //扩容1.5倍扩容int newCapacity = oldCapacity + (oldCapacity >> 1); //极端情况过滤 : 新的容量 - 旧的容量小于0【int值移除】 if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//不扩容了 //新的容量,比ArrayList的最大值,还要大if (newCapacity - MAX_ARRAY_SIZE > 0)//设置新的容量为ArrayList的最大值,以ArrayList最大值为当前容量 newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity);
}

总结:

  1. 扩容的规则并不是翻倍,是原来容量的1.5倍
  2. ArrayList的数组最大值Integer.MAX_VALUE。不允许超过这个最大值
  3. 新增元素时,没有严格的数据值的检查。所有可用设置null

三、ArrayList线程安全问题及解决方案

3.1 错误复现

ArrayList 我们都知道底层是以数组方式实现的,实现了可变大小的数组,它允许所有元素,包括null。
看下面一个例子:开启多个线程操作List集合,向ArrayList中增加元素,同时去除元素

  //全局线程共享集合ArrayListprotected static ArrayList<Object> arrayList = new ArrayList<>();@Testvoid arrayListTest() {//1.创建线程数组【500】Thread[] threads = new Thread[500];//2.遍历数组,向线程中添加500个线程对象for (int i = 0; i < threads.length; i++) {threads[i] = new MyThread();threads[i].start();//启动线程}//3.遍历线程,等待线程执行完毕【等待所有线程执行完毕】for (int i = 0; i < threads.length; i++) {try {threads[i].join();//等待线程执行完毕} catch (InterruptedException e) {e.printStackTrace();}}//线程执行内容 : 向集合中添加自己的线程名称//4.遍历list集合,获取所有线程的名称for (Object threadName : arrayList) {System.out.println("threadName = " + threadName);}}//线程执行内容,是想集合中添加自己的线程名称class MyThread extends Thread {@Overridepublic void run() {try {//线程休眠1000Thread.sleep(1000);//向集合中添加自己的线程名称【操作共享内容,会出现线程安全问题】arrayList.add(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}}

运行代码结果可知,会出现以下几种情况:

  • ①打印null
  • ②某些线程并未打印
  • ③数组角标越界异常

在这里插入图片描述

3.2 导致ArrayList线程不安全的源码分析

ArrayList成员变量

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable 
{//ArrayList的Object的数组存所有元素。transient Object[] elementData; // non-private to simplify nested class 
access//size变量保存当前数组中元素个数。 private int size;
//...
}
  • ArrayList的Object的数组存储所有元素。
  • size变量保存当前数组中元素个数。

出现线程不安全源码之一 : add()方法

public boolean add(E e) {//确保有容量,如果第一次添加,会初始化一个容量为10的list //size当前集合元素的个数,随着添加的元素递增ensureCapacityInternal(size + 1);  // Increments modCount!! //添加元素elementData[size++] = e; return true;
}

add添加元素,实际做了两个大的步骤:

  1. 判断elementData数组容量是否满足需求
  2. 在elementData对应位置上设置值

线程不安全的隐患【1】,导致③数组下标越界异常
在这里插入图片描述
线程不安全的隐患【2】,导致①Null、②某些线程并未打印
致①Null、②某些线程并未打印深层次原因是因为多线程情况下出现了指令重排和不保证原子性问题。详见Volatile关键字在这里插入图片描述
由此我们可以得出,在多线程情况下操作ArrayList 并不是线性安全的。

3.3 解决方案

第一种方案:使用Vector集合,Vector集合是线程安全的

//线程安全问题解决方案1
protected static Vector<Object> vector = new Vector<>();

第二种方案:使用Collections.synchronizedList。它会自动将我们的list方法进行改变,最后返回给我们加锁了List

//线程安全问题解决方案2 
//将集合改为同步集合
protected static List<Object> synList = Collections.synchronizedList(arrayList);

第三种方案:使用JUC中的CopyOnWriteArrayList类进行替换。

//线程安全问题解决方案3 JUC 【最佳选择】
protected static CopyOnWriteArrayList<Object> copyOnWriteArrayList = new 
CopyOnWriteArrayList<>();

因为CopyOnWriteArrayList中的add操作使用了lock锁保证了原子性同时保证线程安全,而且数组也使用了volatile关键字,保证可见性和防止指令重排

在这里插入图片描述
在这里插入图片描述

四、ArrayList的Fail-Fast机制深入理解

什么是Fail-Fast机制?
"快速失败"即Fail-Fast机制,它是Java中一种错误检测机制!
当多钱程对集合进行结构上的改变,或者在迭代元素时直接调用自身方法改变集合结构而没有通知迭代器时,有可能会触发Fail-Fast机制并抛出异常【ConcurrentModificationException】。注意,是有可能
触发Fail-Fast,而不是肯定!

触发时机 : 在迭代过程中,集合的结构发生改变,而此时迭代器并不知情,或者还没来得及反应,便会
产生Fail-Fast事件。

再次强调,迭代器的快速失败行为无法得到保证!一般来说,不可能对是否出现不同步并发修改,或者
自身修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。

Java.util包中的所有集合类都是快速失败的,而java.util.concurrent包中的集合类都是安全失败的;快
速失败的迭代器抛出ConcurrentModificationException,而安全失败的迭代器从不抛出这个异常。

ArrayList的Fast-Fail事件复现及解决方案

    /*** 目标: 复现Fast_Fail机制* 1.产生条件 :*  当多线程操作同一个集合*  同时遍历这个集合,该集合被修改!* 2.解决方案 :使用并发编程包中的集合,CopyOnWriteArrayList替换原有ArrayList集合。*///定义全局共享集合  :static ArrayList<String> list = new ArrayList<>();//Fast_Fail机制CopyOnWriteArrayList//    static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();@Testvoid arrayListTest() {//创建线程1,并且向集合添加元素,打印集合中的内容Thread thread1 = new Thread(() -> {//并且向集合添加元素for (int i = 0; i < 6; i++) {list.add("" + i);// 打印集合中的内容printAll();}});thread1.start();//启动线程1//创建线程2,并且向集合添加元素,打印集合中的内容Thread thread2 = new Thread(() -> {//并且向集合添加元素for (int i = 10; i < 16; i++) {list.add("" + i);// 打印集合中的内容printAll();}});thread2.start();//启动线程2}/*** 使用迭代器打印集合*/public static void printAll() {//获取当前集合的迭代器Iterator<String> iterator = list.iterator();//通过迭代器遍历集合while (iterator.hasNext()) {String value = iterator.next();System.out.println(value + ",");}}

使用ArrayList在多线程情况下添加元素时出线ConcurrentModificationException
在这里插入图片描述
解决办法使用并发编程包中的集合,CopyOnWriteArrayList替换原有ArrayList集合。

扩展连接
ArrayList集合底层原理
ArrayList集合特点为什么是增删慢、查询快

相关文章:

ArrayList源码分析

ArrayList源码分析目标:一、 ArrayList的简介二、ArrayList原理分析2.1 ArrayList的数据结构源码分析2.2 ArrayList默认容量&最大容量2.3 为什么ArrayList查询快&#xff0c;增删慢&#xff1f;2.4 ArrayList初始化容量1、创建ArrayList对象分析&#xff1a;无参数2、创建A…...

SpringBoot IOC、DI、@Autowired、@Resource、作用域

一、初识Spring1.1 Spring是什么Spring是一个轻量级Java开发框架&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的开源框架&#xff0c;为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构&#xff0c;Java开发者可以专…...

链表相关oj题

1.Leetcode203 移除链表元素 解题思路&#xff1a;从头节点开始进行元素删除&#xff0c;每删除一个元素&#xff0c;需要重新链接节点 struct ListNode* removeElements(struct ListNode* head, int val){struct ListNode*dummyheadmalloc(sizeof(struct ListNode));dummyhea…...

【Linux】操作系统(Operator System)

操作系统&#xff08;Operator System &#xff09;一、操作系统的概念二、操作系统的作用三、系统调用和库函数一、操作系统的概念 操作系统是一组控制和管理计算机软硬件资源&#xff0c;为用户提供便捷使用的计算机程序的集合&#xff0c;是配置在计算机硬件系统上的第一层…...

机器学习自学笔记——感知机

感知机预备知识 神经元 ​ 感知机算法最初是由科学家从脑细胞的神经凸起联想而来。如下图&#xff0c;我们拥有三个初始xxx值&#xff0c;x1,x2,x0x_1,x_2,x_0x1​,x2​,x0​。其中x01x_01x0​1为一个初始的常量&#xff0c;专业上称作“偏置”。每个xxx的值都会乘上一个权重…...

C++ Primer第五版_第三章习题答案(21~30)

文章目录练习3.21练习3.22练习3.23练习3.24练习3.25练习3.26练习3.27练习3.28练习3.29练习3.30练习3.21 请使用迭代器重做3.3.3节的第一个练习。 #include <vector> #include <iterator> #include <string> #include <iostream>using std::vector; usi…...

colmap+openmvs进行三维重建流程全记录

window下的colmapopenmvs进行三维重建流程全记录 1.colmap安装与配置 可参考&#xff1a;https://blog.csdn.net/weixin_44153180/article/details/129334018?spm1001.2014.3001.5501 2.openmvs安装与配置 可参考&#xff1a;https://blog.csdn.net/rdw1246010462/article…...

yolov8命令行运行参数详解

序言 整理来自yolov8官方文档常用的一些命令行参数&#xff0c;官方文档YOLOv8 Docs yolov8命令行的统一运行格式为&#xff1a; yolo TASK MODE ARGS其中主要是三部分传参&#xff1a; TASK(可选) 是[detect、segment、classification]中的一个。如果没有显式传递&#xf…...

分布式锁简介

Redis因为单进程、性能高常被用于分布式锁&#xff1b;锁在程序中作用是同步工具&#xff0c;保证共享资源在同一时刻只能被一个线程访问。 Java中经常用的锁synchronized、Lock&#xff0c;但是Java的锁智能保证单机的时候有效&#xff0c;分布式集群环境就无能为力了&#xf…...

【嵌入式Linux学习笔记】Linux驱动开发

Linux系统构建完成后&#xff0c;就可以基于该环境方便地进行开发了&#xff0c;相关的开发流程与MCU类似&#xff0c;但是引入了设备树的概念&#xff0c;编写应用代码要相对复杂一点。但是省去了很多配置工作。 学习视频地址&#xff1a;【正点原子】STM32MP157开发板 字符…...

2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛)(H题)(线段树)

又到了万物复苏的季节&#xff0c;家乡的苹果树结果了。像往常一样小龙同学被叫回家摘苹果。 假设需要采摘的一棵树上当前有a颗苹果&#xff0c;那么小龙会采摘⌈a/3⌉颗苹果&#xff0c;其中⌈x⌉表示不小于x的最小整数。 但是&#xff0c;为了可持续发展&#xff0c;若a小于1…...

Linux内核Thermal框架详解十三、Thermal Governor(3)

接前一篇文章Linux内核Thermal框架详解十二、Thermal Governor&#xff08;2&#xff09; 二、具体温控策略 上一篇文章介绍并详细分析了bang_bang governor的源码。本文介绍第2种温控策略&#xff1a;fair_share。 2. fair_share fair_share governor总的策略是频率档位⽐较…...

TikTok品牌出海创世纪(二)

目录 1.推荐算法打造王者品牌 2.品牌聚焦海外Z群体 3.持续扩展应用场景 加速品牌全球化传播 品牌聚焦海外Z群体 “这个地球上&#xff0c;三分之二的人都在用Facebook“&#xff0c;这是对Facebook曾经统治地位最直观的描述。 但如今&#xff0c;这家全球社交媒体巨头的光环正…...

iOS中SDK开发 -- cocoapods库创建

在iOS项目中&#xff0c;经常使用cocoadpods来进行依赖管理以及三方库引入等。引入的三方库一般会有几种形式&#xff1a;一、在Pods目录下可以直接看到源代码的开源库&#xff0c;如AFNetworking&#xff0c;Masonry等常见开源库。二、在Pods目录下拉取的项目文件只能看到对应…...

2023年了,还是没学会内卷....

先做个自我介绍&#xff1a;我&#xff0c;普本&#xff0c;通信工程专业&#xff0c;现在飞猪干软件测试&#xff0c;工作时长两年半。 回望疫情纪元&#xff0c;正好是实习 毕业这三年。要说倒霉也是真倒霉&#xff0c;互联网浪潮第三波尾巴也没抓住&#xff0c;狗屁造富神…...

chatGPT爆火,什么时候中国能有自己的“ChatGPT“

目录 引言 一、ChatGPT爆火 二、中国何时能有自己的"ChatGPT" 三、为什么openai可以做出chatGPT? 四、结论 引言 随着人工智能技术的不断发展&#xff0c;自然语言处理技术也逐渐成为了研究的热点之一。其中&#xff0c;ChatGPT作为一项领先的自然语言处理技术…...

【Matlab算法】粒子群算法求解一维非线性函数问题(附MATLAB代码)

MATLAB求解一维非线性函数问题前言正文函数实现&#xff08;可视化处理&#xff09;可视化结果前言 一维非线性函数是指函数的自变量和因变量都是一维实数&#xff0c;而且函数的形式是非线性的&#xff0c;也就是不符合线性函数的形式。在一维非线性函数中&#xff0c;自变量…...

2023 最新发布超全的 Java 面试八股文,整整 1000道面试题,太全了

作为一名优秀的程序员&#xff0c;技术面试都是不可避免的一个环节&#xff0c;一般技术面试官都会通过自己的方式去考察程序员的技术功底与基础理论知识。 2023 年的互联网行业竞争越来越严峻&#xff0c;面试也是越来越难&#xff0c;很多粉丝朋友私信希望我出一篇面试专题或…...

产品经理面经|当面试官问你还有什么问题?

相信很多产品经理在跳槽面试的时候&#xff0c;在面试尾声都会遇到这样的环节&#xff0c;面试官会问你有什么问题要问的&#xff0c;一般来说大家都能随时随地甩出几个问题来化解&#xff0c;但其实在这个环节对于应聘者来说也是一个很好的机会来展现自己的能力&#xff0c;甚…...

单链表的基本操作

目录 一.链表的基本概念和结构 二.链表的分类 三.单链表的基本操作 1.创建一个节点 2.打印 3.尾插 4.头插 5.尾删 6.头删 7.查找 8.指定位置插入 9.指定位置删除 10.销毁 一.链表的基本概念和结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解

文章目录 一、开启慢查询日志&#xff0c;定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

TCP/IP 网络编程 | 服务端 客户端的封装

设计模式 文章目录 设计模式一、socket.h 接口&#xff08;interface&#xff09;二、socket.cpp 实现&#xff08;implementation&#xff09;三、server.cpp 使用封装&#xff08;main 函数&#xff09;四、client.cpp 使用封装&#xff08;main 函数&#xff09;五、退出方法…...

电脑桌面太单调,用Python写一个桌面小宠物应用。

下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡&#xff0c;可以响应鼠标点击&#xff0c;并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...

如何把工业通信协议转换成http websocket

1.现状 工业通信协议多数工作在边缘设备上&#xff0c;比如&#xff1a;PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发&#xff0c;当设备上用的是modbus从站时&#xff0c;采集设备数据需要开发modbus主站&#xff1b;当设备上用的是西门子PN协议时&#xf…...