【学习笔记】JDK源码学习之ArrayList(附带面试题)

news2025/1/16 14:50:43

【学习笔记】JDK源码学习之ArrayList(附带面试题)

引言:

什么是 ArrayList ?它和 List 又有什么关系?两者又有什么区别?

带着以上问题让我们来深入走进 ArrayList

1、ArrayList的使用

demo:

//创建一个ArrayList数组
List<String> list = new ArrayList<>();

//向ArrayList数组中添加元素
list.add("Test");
list.add("test");
System.out.println(list);
//输出["Test","test"]

//获取ArrayList中的元素
list.get(index);

//删除ArrayList中的元素
list.remove();

//....

2、ArrayList的继承体系

继承图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g7Jbldsd-1670420719476)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20221206104053134.png)]

源码:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
  //.....
}

继承图源码 中我们能看见 ArrayList 实现了 List、RandomAccess、Cloneable、Serializable 等。

  • 实现了 List 可以表明 ArrayList 具有增删改查和遍历等操作。
  • 实现了 RandomAccess 表明 ArrayList 具有随机访问等能力。
  • 实现了 Cloneable 表明 ArrayList 可以被 浅拷贝
  • 实现了 Serializable 表明 ArrayList 可以被序列化。

3、实现List、RandomAccess、Cloneable、Serializable 的具体表现

实现Cloneable接口,可以进行浅拷贝。

创建一个实体类:


public class User {
    private String name;
    public User(){};
    public User(String name){this.name = name;}

    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
    @Override
    public String toString(){
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }


}

拷贝ArrayList:

        ArrayList list = new ArrayList<>();
        User xiaobao = new User("xiaobao");
        list.add("tset"); //添加字符类型
        list.add(xiaobao); //添加对象类型

        ArrayList clone = (ArrayList)list.clone();
        System.out.println("两者地址对比" + (clone == list));

        //两者里面的元素地址对比
        System.out.println("两者元素值的对比" + (clone.get(1) == list.get(1)));


    }

输出:

两者地址对比false
两者元素值的对比true

从上述结果我们可以发现:

  • 如果进行拷贝其实是重新创建了一个新的 ArrayList 对象。两者间地址引用不相同。
  • 但是两者间的元素引用地址是相同的。
  • 而如果进行深拷贝时, 就会在list被克隆新创建克隆对象时,对其存储的复杂对象类型也进行对象新创建 ,复杂对象类型数据获得新的地址引用,而不是像浅拷贝那样,仅仅拷贝了复杂对象类型的地址引用。

实现RandomAccess接口:

一般实现 RandomAccess 时会提高列表的访问顺序。

接下来我们用 一个小 demo 来对比一下:

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

public class demo02 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        for (int i = 0; i < 9999999; i++) {
            list.add(i);
        }
        // 随机遍历
        long Start = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
                list.get(i);
        }
        long End = System.currentTimeMillis();
        System.out.println("随机遍历时间:" + (End - Start));

        // 有序遍历
        long start = System.currentTimeMillis();
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            iterator.next();
        }
        long end = System.currentTimeMillis();
        System.out.println("顺序遍历时间:" + (end - start));

    }
}

输出:

随机遍历时间:2
顺序遍历时间:3

从上述结果和代码能看出来,随机遍历是比顺序遍历的效率是高一些的。

如果我们使用没有实现 RandomAccess 接口的 LinkedList 来测试一下:

demo:

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

public class demo03 {
    public static void main(String[] args) {
        LinkedList list = new LinkedList<>();
        for (int i = 0; i < 9999; i++) {
            list.add(i);
        }
        // 随机遍历
        long Start = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            list.get(i);
        }
        long End = System.currentTimeMillis();
        System.out.println("随机遍历时间:" + (End - Start));

        // 有序遍历
        long start = System.currentTimeMillis();
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            iterator.next();
        }
        long end = System.currentTimeMillis();
        System.out.println("顺序遍历时间:" + (end - start));

    }
}

运行结果:

随机遍历时间:49
顺序遍历时间:1

可从上述对比和测试结果中能发现,没有实现 RandomAccessLinkedList 的访问是十分低的。

4、ArrayList中的变量和方法

4.1 基础变量

private static final int DEFAULT_CAPACITY = 10;//数组默认初始容量
 
private static final Object[] EMPTY_ELEMENTDATA = {};//定义一个空的数组实例以供其他需要用到空数组的地方调用 
 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//定义一个空数组,跟前面的区别就是这个空数组是用来判断ArrayList第一添加数据的时候要扩容多少。默认的构造器情况下返回这个空数组 
 
transient Object[] elementData;//数据存的地方它的容量就是这个数组的长度,同时只要是使用默认构造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA )第一次添加数据的时候容量扩容为DEFAULT_CAPACITY = 10 
 
private int size;//当前数组的长度


  • DEFAULT_CAPACITY: 默认容量,默认为10,一般使用 new ArrayList() 创建 List 集合实例默认是10。
  • EMPTY_ELEMENTDATA :空数组,一般可以通过 new ArrayList(0) 创建实例中使用的空数组。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA: 定义一个空数组,跟前面的区别 就是这个空数组是用来判断ArrayList第一添加数据的时候要扩容多少 。默认的构造器情况下返回这个空数组 。
  • elementData: 数据存的地方它的容量就是这个数组的长度,同时只要是使用默认构造器( DEFAULTCAPACITY_EMPTY_ELEMENTDATA )第一次添加数据的时候容量扩容为 DEFAULT_CAPACITY = 10

4.2 三种构造方法

源码:

    /**
     * Constructs an empty list with the specified initial capacity.
     *
     * 构造具有指定初始容量的空列表。
     * @param  initialCapacity  the initial capacity of the list 列表初始容量
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     * 传入初始容量,如果大于0就初始化elementData为对应大小,如果等于0就使用EMPTY_ELEMENTDATA空数组,
 		 * 如果小于0抛出异常。
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    /**
     * Constructs an empty list with an initial capacity of ten.
     * 构造一个初始容量为10的空列表。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     * 按照集合的迭代器返回元素的顺序构造一个包含指定集合元素的列表。
     *
     * @param c the collection whose elements are to be placed into this list 
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        // 将构造方法中的集合参数转换成数组
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
          	// 检查c.toArray()返回的是不是Object[]类型,如果不是,重新拷贝成Object[].class类型
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                // 数组的创建与拷贝
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

4.3 扩容机制

大致流程图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GPj5FoFu-1670420719476)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20221206162404498.png)]

结合着上图,我们再来看看 ArrayList 中的源代码。

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 判断是否需要扩容
        elementData[size++] = e;
        return true;
    }


    private static int calculateCapacity(Object[] elementData, int minCapacity) {
       // 判断当前数组是否是默认构造方法生成的空数组,如果是的话minCapacity=10反之则根据原来的值传入下一个方法去完成下一步的扩容判断
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        // 快速报错机制
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
		// ArrayList数组中最大的值为Integer的最大值- 8
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
      	// 获取当前数组的长度
        int oldCapacity = elementData.length;
      	// 把当前的数组长度扩展为之前的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
      	// 进行判断,如果扩容后还是小于所需长度,则把扩容长度变为最小长度。
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
      	// 如果所需最小扩容长度大于ArrayList中最大的长度
        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);
    }

    private static int hugeCapacity(int minCapacity) {
      	// 如果越界,则抛出 OutOfMemoryError 异常
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
      	// 判断如果最小值在 Integer.MAX_VALUE - 8 到 Integer.MAX_VALUE 范围就直接使用其中的范围值。
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

通过上述 流程图源码 的结合,我们能发现一下核心点:

  • int newCapacity = oldCapacity + (oldCapacity >> 1); 扩容的计算方式。一看是 1.5 倍,其实是不完全正确的。因为oldCapacity >> 1必须是整数。

    比如:第一次扩容:应该是 0 + 0/2 = 0 ,

    然后通过 if (newCapacity - minCapacity < 0) newCapacity = minCapacity;将默认值10 赋值给newCapacity。所以无参构造第一次扩容到10。

    第二次扩容:是 10 + 10/2 = 15;

    第三次扩容: 15 +15/2 = 22; 注意15/2 = 7 ,不是7.5。

    通过第三步扩容可以得知,扩容倍数不是1.5倍。实际上是 a = a+a/2。当a 为偶数时,才是1.5倍。

    所以所说,只能是 约等于1.5倍

  • if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);} 判断当前数组是否是默认构造方法生成的空数组,如果是的话minCapacity=10反之则根据原来的值传入下一个方法去完成下一步的扩容判断。

4.4 其他方法

add(int index, E element):

首先我们来看看这个方法的源码:

    /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
      	// 判断是否越界
        rangeCheckForAdd(index);
				// 判断是否需要扩容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
      	// 将元素添加到数组中
        elementData[index] = element;
        size++;
    }

    /**
     * A version of rangeCheck used by add and addAll.
     */
    private void rangeCheckForAdd(int index) {
      	// 如果越界则抛出异常
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
  • 首先 add 方法是会判断这个元素的下标会不会越界,如果越界则会抛出 IndexOutOfBoundsException 异常。
  • 然后在判断是否需不需要扩容。
  • 如果过程中没有抛出异常的话,则正常进行元素的添加 elementData[index] = element

addAll(Collection<? extends E> c):

源码展示:

    /**
     * Appends all of the elements in the specified collection to the end of
     * this list, in the order that they are returned by the
     * specified collection's Iterator.  The behavior of this operation is
     * undefined if the specified collection is modified while the operation
     * is in progress.  (This implies that the behavior of this call is
     * undefined if the specified collection is this list, and this
     * list is nonempty.)
     *
     * @param c collection containing elements to be added to this list
     * @return <tt>true</tt> if this list changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     */
    public boolean addAll(Collection<? extends E> c) {
      	// 将c集合转化为数组
        Object[] a = c.toArray();
        int numNew = a.length;
      	// 检查是否需要扩容
        ensureCapacityInternal(size + numNew);  // Increments modCount
      	// 将c中元素全部拷贝到数组的最后
        System.arraycopy(a, 0, elementData, size, numNew);
      	// 集合中长度的大小增加c的大小
        size += numNew;
      	// 如果c不为空就返回true,否则返回false
        return numNew != 0;
    }
  • 拷贝c中的元素到数组a中;
  • 检查是否需要扩容;
  • 把数组a中的元素拷贝到elementData的尾部;

get(int index):

源码展示:

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
  • 首先判断 index 是否越界。
  • 如果没有则返回 elementData 中的元素。

remove(int index)

源码展示:

    /**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
      	// 检查index是否越界
        rangeCheck(index);

        modCount++;
      	// 获取index位置的元素
        E oldValue = elementData(index);
				// numMoved 向下减1
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
  • 检查索引是否越界;
  • 获取指定索引位置的元素;
  • 如果删除的不是最后一位,则其它元素往前移一位;
  • 将最后一位置为null,方便GC回收;
  • 返回删除的元素。

注意:从源码中得出,ArrayList删除元素的时候并没有缩容

remove(E e):

源码展示:

    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
      	// 将最后一个元素删除,帮助GC
        elementData[--size] = null; // clear to let GC do its work
    }
  • 首先判断需要删除的值是否为 null
  • 如果删除的值是 null 则循环遍历 elementData 数组,且使用 == 来判断。
  • 如果不为 null ,则也是循环遍历 elementData 数组,但是本次判断则是需要使用 equal() 方法来进行判断。
  • 如果不存在该值,则返回 false

fastRemove : 整个删除逻辑是,把index下标后面的元素全部复制提前一位,然后将最后一个元素置为null,自然就会被GC回收.

5、ArrayList常见面试题

5.1、 ArrayList的扩容机制是什么样子的?

5.2、 ArrayList是线程安全的吗?

5.3、 ArrayList 和 LinkedList 的区别?

5.4、 ArrayList 如何解决频繁扩容带来的效率问题?

5.5、 ArrayList插入或删除元素是否一定比LinkedList慢?

5.6、 如何复制某个ArrayList到另外一个ArrayList中去呢?你能列举几种?

答案:
地址

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

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

相关文章

动态照片怎么制作?推荐几种操作简单的制作方法

动态照片应该怎么弄呢&#xff1f;不知道大家的家里有没有那种家庭相册&#xff0c;里面会有一些爷爷奶奶、爸爸妈妈们以前的照片&#xff0c;翻看这些照片的时候&#xff0c;就会想到那个时候的他们。不过相册里的照片基本上是一成不变的&#xff0c;有时候我会想&#xff0c;…

手写Spring6(实现应用上下文)

文章目录目标设计流程项目结构一、实现1、定义实例化前-BeanFactoryPostProcessor2、定义初始化前后-BeanPostProcessor3、定义上下文接口--ApplicationContext4、应用上下文抽象类实现--AbstractBeanFactory5、获取Bean工厂和加载资源--AbstractRefreshableApplicationContext…

webpack学习-cdn加速,使用 Tree Shaking,提取公共代码,分割代码按需加载 使用 Prepack开启 Scope Hoisting

4-9 CDN 加速 什么是 CDN 虽然前面通过了压缩代码的手段来减小网络传输大小,但实际上最影响用户体验的还是网页首次打开时的加载等待。 导致这个问题的根本是网络传输过程耗时大,CDN 的作用就是加速网络传输。 CDN 又叫内容分发网络,通过把资源部署到世界各地,用户在访问…

Mentor-dft 学习笔记 day43-Power-Aware DRC and ATPG

Power-Aware DRC and ATPG 本章介绍用于ATPG工具的power-aware DRC和ATPG流程。Power-Aware Overview 电子行业在设计连续体的主要方面采用了低功耗特性。EDA供应商和主要半导体公司定义了常用的电力数据标准格式来描述电力需求&#xff1a;UPF和CPF。Tessent Shell supports t…

Centos7迁移Anolis OS7系统

2020年12月08日CentOS官方宣布CentOS项目将停止&#xff0c;并推出CentOS Stream项目&#xff0c;详见公告 CentOS未来将会从 RedHat Enterprise Linux(RHEL) 复刻版本的 CentOS Linux 转向 CentOS Stream。 对处于生命周期中的 CentOS 版本后续影响&#xff1a; • CentOS Lin…

Codeforces Round #838 (Div. 2) A-C题解

cf比赛链接 目录 A. Divide and Conquer 题意&#xff1a; 思路&#xff1a; 代码&#xff1a; B. Make Array Good 题意&#xff1a; 思路&#xff1a; 代码&#xff1a; C. Binary Strings are Fun&#xff08;什么疑惑题面&#xff09; 题意&#xff1a;首先的两个…

PG::Sumo

nmap -Pn -p- -T4 --min-rate1000 192.168.170.87 nmap -Pn -p 22,80 -sCV 192.168.170.87 查看80端口的页面&#xff0c;没有什么有用的信息 尝试路径爆破&#xff0c;发现了/cgi-bin目录&#xff0c;就想到了HTB中的Shocker靶机。 继续爆破 wfuzz -c -z file,/usr/share/…

03. SQL注入漏洞基础

03. SQL注入漏洞基础 SQL注入漏洞基础&#xff08;上&#xff09; /01 SQL注入的原理 SQL注入原理 SQL注入产生的原因 当Web应用向后台数据库传递SQL语句进行数据库操作时。如果对用户输入的参数没有经过严格的过滤处理&#xff0c;那么攻击者就可以构造特殊的SQL语句&…

泓德基金:以超融合构建生产及灾备环境,承载 O32 等关键业务系统

案例亮点 承载 O32、TA、估值等基金行业关键业务系统生产与灾备环境。 POC 期间对超融合应用承载能力以及数据库支撑能力分别进行验证&#xff0c;性能与稳定性均满足需求。 超融合首先作为灾备资源池部署&#xff0c;稳定运行一年后&#xff0c;转为生产资源池&#xff0c;并…

高企认定没专利?专利评分低?如何评分?

众所周知&#xff0c;高企申报是一场“持久战”&#xff0c;申报知识产权、归集研发费用、科技成果转化等工作都需要一定的准备时间。其中&#xff0c;知识产权的获取所需要的时间是最长的(两年左右)&#xff0c;对高新认定评分的影响也是最大的。因此&#xff0c;知识产权的研…

来聊一聊 ElasticSearch 最新版的 Java 客户端

可能不少小伙伴都注意到了&#xff0c;从 ElasticSearch7.17 这个版本开始&#xff0c;原先的 Java 高级客户端 Java High Level REST Client 废弃了&#xff0c;不支持了。老实说&#xff0c;ElasticSearch 算是我用过的所有 Java 工具中&#xff0c;更新最为激进的一个了&…

Unity中的Mask组件增加DrawCall的原因

Unity中的Mask组件增加DrawCall的原因 简介 常说mask组件不要常用&#xff0c;因为会增加drawcall&#xff0c;增加性能消耗&#xff1b;当然作为一个需要背八股文的同学而言&#xff0c;仅仅知道会增加性能消耗是不够的&#xff0c;所以这里简单看下其原理。 首先看下在Uni…

监控系列(三)自定义DM采集项(exporter)+主机监控+grafana展示

一、概括 本篇不涉及达梦数据库搭建&#xff0c;操作环境需提前准备prometheus以及grafana的搭建&#xff0c;请跳转到前文查看 监控系列&#xff08;一&#xff09;DM8PrometheusGrafana搭建 监控系列&#xff08;二&#xff09;Dem对接Prometheusgrafana显示 自定义的采集…

基于数字孪生技术的智慧变电站Web3D可视化系统

今天为大家分享一个采用 数维图 的 Sovit3D 构建轻量化 3D 可视化场景的案例——数字孪生智慧变电站三维可视化系统。多维度呈现变电站场景&#xff0c;实现变电站运行态势的实时监测&#xff0c;运维设备、控制系统和信息系统的互联互通。加强变电站设备的全状态感知力与控制力…

03-MySQL查询数据

目录 DQL语言 单表查询 AS子句 DISTINCT关键字的使用 WHERE条件语句 逻辑操作符 比较操作符 BETWEEN范围查询 LIKE模糊查询 使用IN进行范围查询 NULL空值条件查询 连接查询&#xff08;多表查询&#xff09; INNER JOIN内连接 等值和非等值的连接查询 外连接 JOIN对比…

49.Python的while循环

49.Python的while循环 文章目录49.Python的while循环1. 什么是循环2. 什么是while循环3.课题导入4.while循环语法5.while循环执行流程6. if和while的区别7.课堂练习1. 什么是循环 【循环的百度释义】 特指运行一周而回到原处&#xff0c;再转。 反复地连续地做某事。 【循环…

新征程-猿如意试用一波!

猿如意传送门&#xff08;必带&#xff09; 猿如意下载地址&#xff1a;猿如意-程序员的如意兵器,工具代码,一搜就有 猿如意使用了几次了&#xff0c;今天来想分享一下我对于猿如意的使用感受吧&#xff01;&#xff01; 先说结论&#xff1a;值得每个程序员都在电脑里安装一…

MySQL的基础架构简述

文章目录一、一条SQL查询语句是如何执行的1、连接器2、查询缓存3、分析器4、优化器5、执行器一、一条SQL查询语句是如何执行的 开篇先上基本架构示意图&#x1f917;&#xff1a; 大体来说&#xff0c;MySQL可以分为 Server 层和存储引擎两部分。 Server 层包括连接…

大一作业HTML网页作业 HTML校园篮球网页作业(12个页面)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

前沿系列--简述Diffusion Model 扩散模型(无代码版本)

文章目录前言why扩散简述how如何扩散逆向过程小结流程训练过程预测过程总结前言 OK&#xff0c;今天的话&#xff0c;我们来搞一下这个扩散模型&#xff0c;来对这个玩意进行一个简单的了解&#xff0c;因为这个也是目前还算比较前沿的东西&#xff0c;也挺有用的&#xff0c;…