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

news2024/9/23 20:23:52

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

什么是 Vector ?它的作用是什么?它的底层由什么组成?是否是线程安全的?

老样子,跟着上面的问题,我们层层深入了解 Vector 吧。

1、什么是Vector?

VetcorArrayList 相似,其内部都是通过一个容量能够动态增长的 数组 来实现的。不同点是 Vector 是线程安全的。因为其内部有很多同步代码快来保证线程安全.
JDK 中原文有一句注释:

Vector is synchronized. If a thread-safe implementation is not needed, it is recommended to use ArrayList in place of Vector

表示如果不使用线程安全则可以使用 ArrayList ,因其效率比 Vector 高出不少。但是 Vector 是线程安全的,所以使用是比较好的。

一般使用 Vector

Vector vector = new Vector<>();

2、Vector的实现接口和继承类

继承图:

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

通过源码来看:

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

从上述源码中我们能发现 Vector 实现了 RandomAccess、Cloneable和Serialzable 等接口。

2.1 继承List类

Vector 继承了 List 类则可以使 Vector 能进行增删改查。底层则是使用 数组 : protected Object[] elementData;

2.2 实现RandomAccess接口

ArrayList 一样,实现 RandomAccess 接口可以增加随机遍历的效率,能大大提高访问 Vector 数组的时间。

具体可参考本篇文章: 地址

2.3 实现Serializable接口

为什么要实现 Serializable 接口呢?首先我们需要知道Java为什么需要序列化和反序列化。搞清楚这两个我们才能去理解为什么 Vector 要去实现 Serializable 这个接口。

之前在写 LinkedList 源码中曾写过具体的介绍,大家详情可以参考本篇文章嗷:地址 。

2.4 实现Cloneable接口

ArrayList 一样,实现了 Cloneable 接口,即 Vector 也可以实现浅拷贝和深度拷贝。

具体例子本篇就不过对赘述,详情可以参考本篇文章:地址 。

3、Vector中的变量、构造函数和重要方法

3.1 Vector中的变量

    /**
     * The array buffer into which the components of the vector are
     * stored. The capacity of the vector is the length of this array buffer,
     * and is at least large enough to contain all the vector's elements.
     *
     * <p>Any array elements following the last element in the Vector are null.
     *
     * @serial
     */
    protected Object[] elementData;

    /**
     * The number of valid components in this {@code Vector} object.
     * Components {@code elementData[0]} through
     * {@code elementData[elementCount-1]} are the actual items.
     *
     * @serial
     */
    protected int elementCount;

    /**
     * The amount by which the capacity of the vector is automatically
     * incremented when its size becomes greater than its capacity.  If
     * the capacity increment is less than or equal to zero, the capacity
     * of the vector is doubled each time it needs to grow.
     *
     * @serial
     */
    protected int capacityIncrement;

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -2767605614048989439L;

  • elementData : 存储元素的数组,初始容量为10
  • elementCount : 记录数组中元素个数
  • capacityIncrement : 自动扩容的大小,即当数组满了之后,就添加capacityIncrement个空间装载元素,如果 capacityIncrement<=0,则扩容时就扩容到目前Vector容量的两倍
  • serialVersionUID该类的序列化版本号

3.2 Vector中的构造函数

Vector 一共有四个构造函数:

  • 指定容量和增长系数
  • 指定容量
  • 不指定,使用默认容量值10
  • 指定集合初始化

3.2.1 Vector(int initialCapacity, int capacityIncrement):

指定容量和增长系数构造函数

源码:

    /**
     * Constructs an empty vector with the specified initial capacity and
     * capacity increment.
     *
     * @param   initialCapacity     the initial capacity of the vector
     * @param   capacityIncrement   the amount by which the capacity is
     *                              increased when the vector overflows
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

过程:

  • 首先判断 initialCapacity(指定容量的值) 是否小于0。如果小于零就抛出 IllegalArgumentException 异常。
  • 然后初始化数组 elementData
  • 接着指定增长系数 capacityIncrement

3.2.2 Vector(int initialCapacity)

指定容量

源码:

    /**
     * Constructs an empty vector with the specified initial capacity and
     * with its capacity increment equal to zero.
     *
     * @param   initialCapacity   the initial capacity of the vector
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

指定初始化容量,增长系数默认为0。

3.2.3 Vector()

源码:

    /**
     * Constructs an empty vector so that its internal data array
     * has size {@code 10} and its standard capacity increment is
     * zero.
     */
    public Vector() {
        this(10);
    }

什么都不指定,默认给的容量是10。

3.2.4 Vector(Collection<? extends E> c)

指定集合初始化

源码:

    /**
     * Constructs a vector 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
     *       vector
     * @throws NullPointerException if the specified collection is null
     * @since   1.2
     */
    public Vector(Collection<? extends E> c) {
        Object[] a = c.toArray();
        elementCount = a.length;
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
            elementData = Arrays.copyOf(a, elementCount, Object[].class);
        }
    }

过程:

  • 先把集合 c 转化为 数组 a
  • 获取数组的长度并赋值给 elementCount
  • 判断如果是 ArrayList ,则直接复制 。
  • 否则需要进行拷贝。

3.3 Vector中重要的方法

3.3.1 add(E e):

源码:

    /**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }

    /**
     * This implements the unsynchronized semantics of ensureCapacity.
     * Synchronized methods in this class can internally call this
     * method for ensuring capacity without incurring the cost of an
     * extra synchronization.
     *
     * @see #ensureCapacity(int)
     */
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

过程:

  • 首先先修改 modCount 增加次数。
  • 判断容量是否足够。
  • 如果不够则进行扩容(下面单独分析 grow() 方法的作用)。
  • 反之将新元素放在最后一个元素,个数增加。

grow():

源码:

    /**
     * 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
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private void grow(int minCapacity) {
        // overflow-conscious code
      	// 以前的容量
        int oldCapacity = elementData.length;
      	// 现在的容量,是以前的容量加上扩展系数,如果扩展系数小于等于0,那么,就是以前的容量的两倍
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
      	// 如果新的容量比最大的容量还要大(虚拟机的数组大小是有最大值的
        if (newCapacity - MAX_ARRAY_SIZE > 0)
          	// 需要处理把最大的容量降低一些
            newCapacity = hugeCapacity(minCapacity);
      	// 拷贝数据
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

过程:

  • 首先先获取之前的容量。
  • 然后在获取现在的容量:现在的容量,是以前的容量加上扩展系数,如果扩展系数小于等于0,那么,就是以前的容量的两倍。
  • 如果最小容量 minCapacity 小于 minCapacity 。则就按照最小容量来扩容。
  • 如果新的容量比最大的容量还要大(虚拟机的数组大小是有最大值的),则就抛出 OutOfMemoryError 异常。
  • 反之进行拷贝扩容。

3.3.2 add(int index, E element)

源码:

    /**
     * Inserts the specified element at the specified position in this Vector.
     * 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 ArrayIndexOutOfBoundsException if the index is out of range
     *         ({@code index < 0 || index > size()})
     * @since 1.2
     */
    public void add(int index, E element) {
        insertElementAt(element, index);
    }

    /**
     * Inserts the specified object as a component in this vector at the
     * specified {@code index}. Each component in this vector with
     * an index greater or equal to the specified {@code index} is
     * shifted upward to have an index one greater than the value it had
     * previously.
     *
     * <p>The index must be a value greater than or equal to {@code 0}
     * and less than or equal to the current size of the vector. (If the
     * index is equal to the current size of the vector, the new element
     * is appended to the Vector.)
     *
     * <p>This method is identical in functionality to the
     * {@link #add(int, Object) add(int, E)}
     * method (which is part of the {@link List} interface).  Note that the
     * {@code add} method reverses the order of the parameters, to more closely
     * match array usage.
     *
     * @param      obj     the component to insert
     * @param      index   where to insert the new component
     * @throws ArrayIndexOutOfBoundsException if the index is out of range
     *         ({@code index < 0 || index > size()})
     */
    public synchronized void insertElementAt(E obj, int index) {
      	// 修改次数增加
        modCount++;
      	// 判断index是否非法
        if (index > elementCount) {
            throw new ArrayIndexOutOfBoundsException(index
                                                     + " > " + elementCount);
        }
      	// 确保容量足够
        ensureCapacityHelper(elementCount + 1);
      	// 拷贝数据,将后面的元素,往后移动一位
        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
      	// 将实际的数据插入
        elementData[index] = obj;
      	// 个数增加
        elementCount++;
    }

过程:

  • 调用插入函数 insertElementAt
  • 增加 modCount 的修改次数。
  • 首先判断 index 是否越界。
  • 接着判断 Vector 中的容量是否够用。
  • 拷贝数据,将后面的元素,往后移动一位。
  • 将实际的数据插入。
  • 个数增加。

因为方法加了 synchronized 关键字可以保证线程安全,但是随之而来的就是效率的下降。

3.3.3 addAll(Collection<? extends E> c)

将一个集合所有元素添加进去

源码:

    /**
     * Appends all of the elements in the specified Collection to the end of
     * this Vector, 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 Vector, and this Vector is nonempty.)
     *
     * @param c elements to be inserted into this Vector
     * @return {@code true} if this Vector changed as a result of the call
     * @throws NullPointerException if the specified collection is null
     * @since 1.2
     */
    public synchronized boolean addAll(Collection<? extends E> c) {
      	// 修改次数增加
        modCount++;
      	// 转成数组
        Object[] a = c.toArray();
      	// 数组的长度
        int numNew = a.length;
      	// 确保容量足够
        ensureCapacityHelper(elementCount + numNew);
      	// 拷贝
        System.arraycopy(a, 0, elementData, elementCount, numNew);
      	// 新增数组长度
        elementCount += numNew;
      	// 返回添加的数组是不是有数据
        return numNew != 0;
    }

过程:

  • 首先增加修改次数 modCount
  • 然后把 c 集合转化为数组并获取数组的长度。
  • 判断原来的数组长度加上现在的数组长度是否容量够用。
  • 如果够用则进行数据拷贝、增加数组长度并返回相关值。

3.3.4 addAll(int index, Collection<? extends E> c)

指定index,插入一个集合,和前面不一样的地方在于复制之前,需要计算往后面移动多少位,不是用for循环去插入,而是一次性移动和写入。

源码:

    /**
     * Inserts all of the elements in the specified Collection into this
     * Vector at the specified position.  Shifts the element currently at
     * that position (if any) and any subsequent elements to the right
     * (increases their indices).  The new elements will appear in the Vector
     * in the order that they are returned by the specified Collection's
     * iterator.
     *
     * @param index index at which to insert the first element from the
     *              specified collection
     * @param c elements to be inserted into this Vector
     * @return {@code true} if this Vector changed as a result of the call
     * @throws ArrayIndexOutOfBoundsException if the index is out of range
     *         ({@code index < 0 || index > size()})
     * @throws NullPointerException if the specified collection is null
     * @since 1.2
     */
    public synchronized boolean addAll(int index, Collection<? extends E> c) {
      	// 增加修改次数
        modCount++;
      	// 判读索引是否越界
        if (index < 0 || index > elementCount)
          	// 如果越界则抛出异常
            throw new ArrayIndexOutOfBoundsException(index);
				// 将集合转化为数组
        Object[] a = c.toArray();
      	// 获取新数组的长度
        int numNew = a.length;
      	// 判断数组容量是否够用
        ensureCapacityHelper(elementCount + numNew);
				// 移动的步长计算
        int numMoved = elementCount - index;
        if (numMoved > 0)
          	// 移动后面的元素,腾出位置给插入的元素
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
				// 插入元素
        System.arraycopy(a, 0, elementData, index, numNew);
      	// 更新个数
        elementCount += numNew;
      	// 插入元素个数是否为0
        return numNew != 0;
    }

3.3.5 remove(Object o)

删除指定元素

源码:

    /**
     * Removes the first occurrence of the specified element in this Vector
     * If the Vector does not contain the element, it is unchanged.  More
     * formally, removes the element with the lowest index i such that
     * {@code (o==null ? get(i)==null : o.equals(get(i)))} (if such
     * an element exists).
     *
     * @param o element to be removed from this Vector, if present
     * @return true if the Vector contained the specified element
     * @since 1.2
     */
	// 实际调用的是removeElement()
    public boolean remove(Object o) {
        return removeElement(o);
    }

	// 线程安全
    public synchronized boolean removeElement(Object obj) {
        // 修改次数增加
        modCount++;
    	// 获取第一个满足条件的元素缩影
        int i = indexOf(obj);
    	// 如果索引满足条件
        if (i >= 0) {
            // 将索引为i的元素从数组中移除
            removeElementAt(i);
            // 返回删除成功的结果
            return true;
        }
    	// 返回失败的结果
        return false;
    }
	// 线程安全
    public synchronized void removeElementAt(int index) {
        // 修改次数加一
        modCount++;
    	// 判断索引是否越界
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
    	// index后面的元素个数
        int j = elementCount - index - 1;
        if (j > 0) {
            // 往前面移动一位(复制,覆盖)
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
    	// 长度减1
        elementCount--;
    	// 将最后一个元素指向null,最后GC会进行回收
        elementData[elementCount] = null; /* to let gc do its work */
    }

过程:

  • 首先找出最近的索引,然后判断索引是否越界。
  • 如果没有越界则获取该索引后的元素个数。
  • 把这些元素往前面移动一位(复制,覆盖)。
  • 原来最后一个元素的位置置空,让GC回收。

3.3.6 remove(int index)

按照索引删除元素

源码:

public synchronized E remove(int index) {
        // 修改次数增加
        modCount++;
        // 合法性判断
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        // 保存原来的数据
        E oldValue = elementData(index);
        // 移动的个数
        int numMoved = elementCount - index - 1;
        // 如果移动个数大于0
        if (numMoved > 0)
            // 后面的元素往前面移动一位,赋值,覆盖
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 最后一个元素置空
        elementData[--elementCount] = null; // Let gc do its work
        // 返回旧的元素
        return oldValue;
    }

过程:

  • 首先判断索引是否越界,如果越界则抛出 ArrayIndexOutOfBoundsException 异常。
  • 保存原来的数据,并获取要移动的元素个数。
  • 后面的元素往前面移动一位,赋值,覆盖。
  • 最后一个元素置空,并让GC回收。

3.3.7 set

下面两个set函数都是,修改索引为index的元素,区别就是一个会返回旧的元素,一个不会返回旧的元素。

public synchronized E set(int index, E element) {
        // 合法性判断
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        // 取出旧的元素
        E oldValue = elementData(index);
        // 更新
        elementData[index] = element;
        // 返回旧的元素
        return oldValue;
    }
    public synchronized void setElementAt(E obj, int index) {
        // 合法哦性判断
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        // 直接更新
        elementData[index] = obj;
    }

3.3.8 get

源码:

public synchronized E get(int index) {
        // 合法判断
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        // 返回数组的元素
        return elementData(index);
    }

// 获取第一个元素:

public synchronized E firstElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(0);
    }

// 获取最后一个元素:

public synchronized E lastElement() {
        if (elementCount == 0) {
            throw new NoSuchElementException();
        }
        return elementData(elementCount - 1);
    }
    E elementData(int index) {
        return (E) elementData[index];
    }

4、Vector常见的面试题

4.1 为什么要成倍的扩容而不是一次增加一个固定大小的容量呢?

4.2 为什么是以两倍的方式扩容而不是三倍四倍,或者其他方式呢?

4.3 Vector 和 ArrayList 的区别又有什么呢?

答案参考:地址

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

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

相关文章

Linux——linux面试题

cat a.txt | cut -d "/" -f 3 | sort | uniq -c |sort -nrgrep ESTABLISHED | awk -F " " {print $5} |cut -d ":" -f 1 | sort |uniq -c | sort -nr找回mysql的root用户的密码 首先&#xff0c;进入到/etc/my.cnf&#xff0c;插入一句skip-gra…

Apache Hudi Timeline

Timeline | Apache Hudi Hudi维护了在不同时刻在表上执行的所有操作的时间线&#xff0c;这有助于提供表的即时视图&#xff0c;同时也有效地支持按到达顺序检索数据。Hudi的核心是维护表上在不同的即时时间&#xff08;instants&#xff09;执行的所有操作的时间轴&#xff08…

windows下配置chrome浏览器驱动的详细攻略

要想使用python去爬取互联网上的数据&#xff0c;尤其是要模拟登录操作。那么selenium包肯定是绕不过的。 selenium包本质上就是通过后台驱动的方式驱动浏览器去。以驱动chrome浏览器为例&#xff0c;搭建环境如下&#xff1a; 1、查看本机chrome浏览器的版本。 方式是&#x…

第三十二章 linux-模块的加载过程二

第三十二章 linux-模块的加载过程二 文章目录第三十二章 linux-模块的加载过程二HDR视图的第二次改写模块导出的符号HDR视图的第二次改写 在这次改写中&#xff0c;HDR视图中绝大多数的section会被搬移到新的内存空间中&#xff0c;之后会根据这些section新的内存地址再次改写…

[附源码]计算机毕业设计Python“小世界”私人空间(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等…

知到/智慧树——程序设计基础(C语言)进阶篇

目录 第一章测试 第二章测试 第三章测试 第四章测试 第五章测试 第一章测试 第1部分总题数: 10 1 【单选题】 (10分) 在C语言中&#xff0c;将属于不同类型的数据作为一个整体来处理时&#xff0c;常用&#xff08; &#xff09;。 A. 简单变量 B. 数组类型数据 C. 结…

论文投稿指南——中文核心期刊推荐(力学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

10.union all、N天连续登录

有日志如下&#xff0c;请写出代码求得所有用户和活跃用户的总数及平均年龄。&#xff08;活跃用户指连续两天都有访问记录的用户&#xff09; 数据准备 最后需完成的结果表 步骤1&#xff0c;所有用户的总数及平均年龄 (1). 将数据去重 with t1 as (select distinctuser_i…

如何使用交换机、路由器及防火墙进行组网以及他们之间的功能和区别

如何使用交换机、路由器及防火墙进行组网以及他们之间的功能和区别。 几乎大部分网络都有交换机、路由器和防火墙这三种基本设备,因此这三种设备对于网络而言非常重要,很多人对这三种设备的使用容易弄混。 一般网络部署: 或者抽象为这种部署模式: 几乎每个网络都有交换…

别再写jsp了,Thymeleaf它不香吗?

啥是 Thymeleaf在学 Thymeleaf 之前我们先看一下使用 jsp 开发遇到的主要问题&#xff1a;jsp 的痛点1.页面包含大量 java 代码&#xff0c;代码太混乱<% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head> &l…

webpack实现自动代码编译

前置 使用webpack构建开发的代码&#xff0c;为了运行需要有两个操作&#xff1a; 操作一&#xff1a;npm run build&#xff0c;编译相关的代码。操作二&#xff1a;通过live server或者直接通过浏览器&#xff0c;打开index.html代码&#xff0c;查看效果。为了完成自动编译&…

《图解TCP/IP》阅读笔记(第五章 5.7、5.8)—— IP隧道与其他IP相关技术

5.7 IP隧道 IP隧道技术顾名思义&#xff0c;是用于在两片网络区域间直接建立通信的通路&#xff0c;而绕过此间的其他网络的一种技术&#xff0c;如下图所示&#xff1a; 网络A与网络B使用IPv6技术&#xff0c;使用IP隧道技术&#xff0c;便可以绕过网络C。 那么其工作原理是…

机器学习 波士顿房价预测 Boston Housing

目录 一&#xff1a;前言 二&#xff1a;模型预测(KNN算法) 三&#xff1a;回归模型预测比对 一&#xff1a;前言 波士顿房价是机器学习中很常用的一个解决回归问题的数据集 数据统计于1978年&#xff0c;包括506个房价样本&#xff0c;每个样本包括波士顿不同郊区房屋的13种…

SQL - MySQL回表

一、回表概念&#xff1b;现象 回表&#xff0c;顾名思义就是回到表中&#xff0c;也就是先通过普通索引&#xff08;我们自己建的索引不管是单列索引还是联合索引&#xff0c;都称为普通索引&#xff09;扫描出数据所在的行&#xff0c;再通过行主键ID 取出索引中未包含的数据…

[附源码]计算机毕业设计Python创新创业管理系统(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

Trie(Trie字符串统计)【实质就是二维数组 表示 链表】【二维数组的第一行就是 头结点】

欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09; 文章字体风格&#xff1a; 红色文字表示&#xff1a;重难点✔ 蓝色文字表示&#xff1a…

探索MapReduce

文章目录一&#xff0c;案例分析&#xff08;一&#xff09;TopN分析法介绍&#xff08;二&#xff09;案例需求二&#xff0c;案例实施&#xff08;一&#xff09;准备数据文件&#xff08;1&#xff09;启动hadoop服务&#xff08;2&#xff09;在虚拟机上创建文本文件&#…

ArcGIS中的OBJECTID、FID 和 OID 的区别!不要傻傻分不清

喜欢就关注我们吧 时常有很多我朋友分不清OBJECTID、FID 和 OID有什么区别&#xff0c;不懂得怎么应用和管理&#xff0c;今天我们来说个明白。 ArcGIS Desktop 产品要求独立表和属性表均具有 ObjectID字段&#xff0c;该字段包含唯一的长整型用于标识每个记录。 此 ID 由 Esri…

第二证券|元宇宙发展规划出炉,3只元宇宙概念股估值创年内新低

本月以来&#xff0c;北上资金加仓23只元国际概念股。 12月15日晚间&#xff0c;浙江省发改委等5部门联合印发《浙江省元国际工业开展举动计划&#xff08;2023—2025年&#xff09;》&#xff08;以下简称《计划》&#xff09;。《计划》中说到&#xff0c;到2025年&#xff0…

查找外文文献必备网站

查找下载外文文献不仅先要找到文献数据库资源&#xff0c;并且还需要有数据库的文献下载权限&#xff0c;如果你没有文献数据库使用权限就用下面这个方法。 文献党下载器&#xff08;wxdown.org&#xff09;有资源有权限&#xff0c;覆盖全科。下面举几个例子&#xff0c;用文…