Collention集合基础知识

news2024/11/26 21:40:52
Array
  • 数组是一种连续的内存空间存储相同数据类型数据的线性数据结构
  1. 数组获取其他元素的地址值

寻址公式
a[i] = baseaddress + i*datatypesize

  1. 为什么数组索引从0开始 从1开始不行吗
    • 从0开始寻址公式

a[i] = baseaddress + i*datatypesize

  • 从1开始寻址公式

a[i] = baseaddress + (i-1)*datatypesize 效率没有从0开始高

  1. 对于cpu来说增加了一个减法指令

根据数组索引获取元素的时候会用索引和寻址公式来计算内存所对应的元素数据
寻址公式为a[i] = baseaddress + i*datatypesize
如果数组从1开始寻址公式 就需要增加一次减法操作 对于cpu来说就多了一个指令

  1. 数组查找时间复杂度
  • 索引查询

数组元素的访问是通过下标来访问 计算机通过数组的首地址和寻址公式 能够很快速的找到想要访问的元素

  • 未知索引查询

如果数组未排序 只能遍历查询每一个数组 O(n)

  1. 如果是已排序的数组 可以通过二分查询 O(logn)
  2. 操作数组的时间复杂度

数组是一段连续的内存空间 因此为了保证数组的连续性会使数组的插入和删除的效率变得很低O(n)

ArrayList

实现源码
成员变量

    /**
     * ArrayList的默认容量,为10。
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 一个空的对象数组,用于表示空的ArrayList实例。
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
        也是一个空的对象数组,
        但用于表示默认大小的空ArrayList实例。
        当向这个ArrayList添加第一个元素时,它会被扩展到DEFAULT_CAPACITY
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
        是一个transient类型的Object数组,用于存储ArrayList的元素。
        ArrayList的容量就是这个数组的长度
     */
    transient Object[] elementData; 
    /**
        ArrayList的大小,即它包含的元素数量。
     */
    private int size;

构造方法

   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);
        }
    }

参数initialCapacity指定列表的初始容量。

如果initialCapacity大于0,则创建一个具有指定容量的空元素数组。

如果initialCapacity等于0,则使用空元素数组EMPTY_ELEMENTDATA。

如果initialCapacity小于0,则抛出IllegalArgumentException异常,提示非法容量。


    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

初始化一个空的ArrayList对象。它将元素数据elementData设置为一个默认容量的空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,以便后续添加元素时能够存储。这个构造函数适用于在创建ArrayList对象时不需要指定初始容量的情况

    public ArrayList(Collection<? extends E> c) {
        Object[] a = c.toArray();
        if ((size = a.length) != 0) {
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

该函数是ArrayList的构造函数,它接受一个Collection类型的参数c,并根据参数c的内容初始化一个ArrayList对象。首先将参数c转换为Object数组a,然后判断数组a的长度是否为0,如果不为0,则判断参数c的类型是否为ArrayList类型,如果是,则直接将数组a赋值给elementData成员变量;如果不是,则通过Arrays.copyOf方法创建一个新的Object数组,并将数组a的内容复制到新数组中,最后将新数组赋值给elementData成员变量。如果数组a的长度为0,则直接将EMPTY_ELEMENTDATA赋值给elementData成员变量

ArrayList.add(i) 分析
public class Array {
    public static void main(String[] args) {
        ArrayList<Integer> array = new ArrayList<>();
        array.add(-1);
    }
}

add()

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

调用ensureCapacityInternal(size + 1)方法来确保集合的容量足够容纳新元素

    private void ensureCapacityInternal(int minCapacity) {
                                计算容量
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

计算容量

    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
private void ensureExplicitCapacity(int minCapacity) {
    modCount++; // 1. 增加修改次数计数器modCount的值,这通常用于实现如ArrayList等集合的fail-fast机制,当在迭代过程中检测到modCount变化时会抛出 ConcurrentModificationException。

    // overflow-conscious code // 2. 注释提示此段代码考虑了溢出情况的处理
    if (minCapacity - elementData.length > 0) // 3. 检查传入的最小容量minCapacity是否大于当前数组elementData的长度
        grow(minCapacity); // 4. 如果确实需要扩容,则调用grow方法。grow方法会负责计算新的容量(通常是旧容量的1.5倍,并处理潜在的溢出问题),然后重新分配数组空间并将原数据复制到新数组中。
}

当之前扩容的值和当前元素数组长度相同进行扩容
容量不够进行扩容

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        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);
    }

步骤1: 获取旧容量
int oldCapacity = elementData.length;
这里 elementData 是 ArrayList 的内部数组,用于存储所有元素。oldCapacity 就是这个数组的当前长度,也就是 ArrayList 的当前容量。
步骤2: 计算新容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
newCapacity 是计算出的新容量。这里使用了一个简单的数学公式:newCapacity = oldCapacity * 1.5。通过位运算 (oldCapacity >> 1) 实现除以2的操作,然后加上 oldCapacity 得到 1.5 * oldCapacity
步骤3: 检查新容量是否满足最小需求
if (newCapacity - minCapacity < 0)
如果新容量不足以满足 minCapacity 的需求,那么直接将 newCapacity 设置为 minCapacity。
步骤4: 检查新容量是否超过最大数组大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
MAX_ARRAY_SIZE 是一个常量,代表数组的最大允许长度。如果 newCapacity 超过了这个限制,那么调用 hugeCapacity(minCapacity) 方法来处理。这个方法会根据 minCapacity 返回一个合适的容量值,确保不会超过 Integer.MAX_VALUE。
步骤5: 扩容并复制数据
elementData = Arrays.copyOf(elementData, newCapacity);
使用 Arrays.copyOf 方法创建一个新的数组,长度为 newCapacity,并将原数组 elementData 的所有元素复制到新数组中。之后,elementData 引用指向新数组,完成扩容过程。

  1. ArrayList
  • 底层数据结构

ArrayList底层是用动态的数组实现的

  • 初始容量

ArrayList初始容量为0,当第一次添加数据的时候才会初始化容量为10

  • 扩容逻辑

ArrayList在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组

  • 添加逻辑
    • 确保数组已使用长度(size)加1之后足够存下下一个数据
    • 计算数组的容量,如果当前数组已使用长度+1后的大于当前的数组长度,则调用grow方法扩容(原来的1.5倍)
    • 确保新增的数据有地方存储之后,则将新元素添加到位于size的位置上。
    • 返回添加成功布尔值。
  1. ArrayList list=new ArrayList(10)中的list扩容几次
    1. 调用grow方法的次数
    2. 带有参数的构造方法并没有调用这个方法 而是创建一个长度为10 的object数组赋值给elementdata
    3. 如果传入的参数为0会将elementdata指向空参empty_elementdata
  2. 如何实现数组和List之间的转换
        //数组转list
        String []strings = {"123","qwe","asd"};
        List<String> list = Arrays.asList(strings);
        //list 转数组
        List<String> array = new ArrayList<>();
        array.add("123");
        array.add("qwe");
        array.add("asd");
        String[] arrayString = list.toArray(new String[list.size()]);
  1. 用arrays.aslist转list后修改了数组内容list会受印象吗


Arrays.asList()转换list之后如果修改了数组内容 list受影响
虽然是new 了一个arrayList 但是这个内部类将我们传入的集合进行了包装 a = Objects.requireNonNull(array); 最终的指向还是同一块内存地址

    @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }
    }
  1. List用toArray转数组后如果修改了List内容数组受印象吗

当调用了toArray以后在底层他还是进行了数组的拷贝

    public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }
  1. ArrayList和LinkedList区别
  2. 底层数据结构
    1. ArrayList是动态数组的数据结构
    2. LinkedList是双向链表的数据结构实现
  3. 操作的效率
    1. 查询
      1. ArrayList按照下标查询的时间复杂度为O(1)
      2. ArrayList 和LinkedList 都需要遍历 时间复杂度都是O(n)
    2. 删除和添加
      1. 删除指定的的都需要遍历链表时间复杂度为O(n)
    3. 内存占用
      1. ArrayList 底层是数组连续内存 节省内春
      2. linkedList是双向链表需要存储数据 和两个指针更占用内存
    4. 线程安全
      1. 都是线程不安全的
        1. 使用可以在方法内使用局部变量是线程安全的
        2. 使用collection.syinzed(new array())
HashMap

hash table 是根据key直接访问内存存储位置值的数据结构
将key映射为数组下标的函数为散列函数 hashvalue = hash(key)
散列函数的基本要求
散列函数计算的到的散列值必须是大于等于0的正整数 hashvlue为数组的下标
如果k1 = k2 那么经过hash后得到的哈希值也必须相同 hash(k1) = hash(k2)
如果k1!=k2那么经过hash后得到的hash值也不必相同 hash(k1)!=hash(k2)
散列冲突
在散列表总数组的每一个下标位置我们可以称之为桶 每一个桶会对应一条链表 所有散列值相同的元素我们都会放到相同槽位相对应的链表中

  1. hashmap实现原理
  • 当我们往hashmap中put元素时利用key的hashcode重新hash计算当前对象在数组中的下标
  • 存储时如果出现hash值现通的key
    • key相同覆盖原始值
    • key不同 将key则将key-vlaue放入链表或红黑树中
    • 1.7
      • 采用的是拉链法 将链表与数组结合 创建一个链表数组 数组中每一格就是一个链表 若遇到哈希冲突则将hash冲突的值加到链表中
    • 1.8
      • 当链表长度大于阈值8时且数组长度达到64时 将链表转化为红黑树 以减少搜索时间 扩容时 红黑树拆成树的节点树小于等于6个则退化成链表
  1. hashmap put 方法的具体流程外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image.png

1
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
2
    /**
     * 实现Map的put方法及其相关操作。
     * 此方法是Map.put操作及其变体的核心实现。
     *
     * @param hash   键的哈希值
     * @param key    键
     * @param value  要放入的值
     * @param onlyIfAbsent 如果为true,则仅在键不存在时才放入值
     * @param evict  如果为false,表示表处于创建模式(此参数目前未使用)
     * @return       与键关联的前一个值,如果之前没有值则返回null
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        // 初始化或必要时调整表大小
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        // 如果槽位为空,尝试直接插入新节点
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            // 处理槽位已占用的情况
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                // 如果节点是树节点,使用树操作进行插入
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                // 对哈希冲突进行线性探测
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // 如果探测长度超过阈值,转换为树节点
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            // 如果找到现有节点,更新值或返回前一个值
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        // 更新大小和modCount,必要时调整大小
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

  1. 判断键值对数组table是否为空或为null,否则执行resize()进行扩容(初始化)
  2. 根据键值key计算hash值得到数组索引
  3. 判断table[i]==null,条件成立,直接新建节点添加
  4. 如果table[i]==null ,不成立4.1 判断table[i]的首个元素是否和key一样,如果相同直接覆盖value4.2 判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对4.3 遍历table[i],链表的尾部插入数据,然后判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操 作,遍历过程中若发现key已经存在直接覆盖value
  5. 插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold(数组长度*0.75),如果超过,进行扩容。
  6. hashmap寻址算法
    1. 首先获取key的hashCode值,然后右移16位 异或运算 原来的hashCode值,主要作用就是使原来的hash值更加均匀,减少hash冲突
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  • 有了hash值之后,就很方便的去计算当前key的在数组中存储的下标
  • &hash : 得到数组中的索引,代替取模,性能更好,数组长度必须是2的n次幂
  1. hashmap扩容机制

image.png


//扩容、初始化数组
final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
    	//如果当前数组为null的时候,把oldCap老数组容量设置为0
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        //老的扩容阈值
    	int oldThr = threshold;
        int newCap, newThr = 0;
        //判断数组容量是否大于0,大于0说明数组已经初始化
    	if (oldCap > 0) {
            //判断当前数组长度是否大于最大数组长度
            if (oldCap >= MAXIMUM_CAPACITY) {
                //如果是,将扩容阈值直接设置为int类型的最大数值并直接返回
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //如果在最大长度范围内,则需要扩容  OldCap << 1等价于oldCap*2
            //运算过后判断是不是最大值并且oldCap需要大于16
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold  等价于oldThr*2
        }
    	//如果oldCap<0,但是已经初始化了,像把元素删除完之后的情况,那么它的临界值肯定还存在,       			如果是首次初始化,它的临界值则为0
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        //数组未初始化的情况,将阈值和扩容因子都设置为默认值
    	else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
    	//初始化容量小于16的时候,扩容阈值是没有赋值的
        if (newThr == 0) {
            //创建阈值
            float ft = (float)newCap * loadFactor;
            //判断新容量和新阈值是否大于最大容量
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
    	//计算出来的阈值赋值
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        //根据上边计算得出的容量 创建新的数组       
    	Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    	//赋值
    	table = newTab;
    	//扩容操作,判断不为空证明不是初始化数组
        if (oldTab != null) {
            //遍历数组
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                //判断当前下标为j的数组如果不为空的话赋值个e,进行下一步操作
                if ((e = oldTab[j]) != null) {
                    //将数组位置置空
                    oldTab[j] = null;
                    //判断是否有下个节点
                    if (e.next == null)
                        //如果没有,就重新计算在新数组中的下标并放进去
                        newTab[e.hash & (newCap - 1)] = e;
                   	//有下个节点的情况,并且判断是否已经树化
                    else if (e instanceof TreeNode)
                        //进行红黑树的操作
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    //有下个节点的情况,并且没有树化(链表形式)
                    else {
                        //比如老数组容量是16,那下标就为0-15
                        //扩容操作*2,容量就变为32,下标为0-31
                        //低位:0-15,高位16-31
                        //定义了四个变量
                        //        低位头          低位尾
                        Node<K,V> loHead = null, loTail = null;
                        //        高位头		   高位尾
                        Node<K,V> hiHead = null, hiTail = null;
                        //下个节点
                        Node<K,V> next;
                        //循环遍历
                        do {
                            //取出next节点
                            next = e.next;
                            //通过 与操作 计算得出结果为0
                            if ((e.hash & oldCap) == 0) {
                                //如果低位尾为null,证明当前数组位置为空,没有任何数据
                                if (loTail == null)
                                    //将e值放入低位头
                                    loHead = e;
                                //低位尾不为null,证明已经有数据了
                                else
                                    //将数据放入next节点
                                    loTail.next = e;
                                //记录低位尾数据
                                loTail = e;
                            }
                            //通过 与操作 计算得出结果不为0
                            else {
                                 //如果高位尾为null,证明当前数组位置为空,没有任何数据
                                if (hiTail == null)
                                    //将e值放入高位头
                                    hiHead = e;
                                //高位尾不为null,证明已经有数据了
                                else
                                    //将数据放入next节点
                                    hiTail.next = e;
                               //记录高位尾数据
                               	hiTail = e;
                            }
                            
                        } 
                        //如果e不为空,证明没有到链表尾部,继续执行循环
                        while ((e = next) != null);
                        //低位尾如果记录的有数据,是链表
                        if (loTail != null) {
                            //将下一个元素置空
                            loTail.next = null;
                            //将低位头放入新数组的原下标位置
                            newTab[j] = loHead;
                        }
                        //高位尾如果记录的有数据,是链表
                        if (hiTail != null) {
                            //将下一个元素置空
                            hiTail.next = null;
                            //将高位头放入新数组的(原下标+原数组容量)位置
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
    	//返回新的数组对象
        return newTab;
    }
  • 在添加元素或初始化的时候需要调用resize方法进行扩容,第一次添加数据初始化数组长度为16,以后每次每次扩容都是达到了扩容阈值(数组长度 * 0.75)
  • 每次扩容的时候,都是扩容之前容量的2倍;
  • 扩容之后,会新创建一个数组,需要把老数组中的数据挪动到新的数组中
    • 没有hash冲突的节点,则直接使用 e.hash & (newCap - 1) 计算新数组的索引位置
    • 如果是红黑树,走红黑树的添加
    • 如果是链表,则需要遍历链表,可能需要拆分链表,判断(e.hash & oldCap)是否为0,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上
  1. hashmap的数组长度一定是2的次幂
    1. &hash : 得到数组中的索引,代替取模,性能更好,数组长度必须是2的n次幂
    2. 扩容时重新计算索引效率更高: hash & oldCap == 0 的元素留在原来位置 ,否则新位置 = 旧位置 + oldCap
  2. hashmap在1.7的情况下的多线程死循环问题
    1. jdk7的的数据结构是:数组+链表
    2. 在数组进行扩容的时候,因为链表是头插法,在进行数据迁移的过程中,有可能导致死循环
  • 变量e指向的是需要迁移的对象
  • 变量next指向的是下一个需要迁移的对象
  • Jdk1.7中的链表采用的头插法
  • 在数据迁移的过程中并没有新的对象产生,只是改变了对象的引用
  1. hashset 与hashmap的区别
  • HashSet实现了Set接口, 仅存储对象; HashMap实现了 Map接口, 存储的是键值对.
  • HashSet底层其实是用HashMap实现存储的, HashSet封装了一系列HashMap的方法. 依靠HashMap来存储元素值,(利用hashMap的key键进行存储), 而value值默认为Object对象. 所以HashSet也不允许出现重复值, 判断标准和HashMap判断标准相同, 两个元素的hashCode相等并且通过equals()方法返回true.
    | 区别 | HashTable | HashMap |
    | — | — | — |
    | 数据结构 | 数组+链表 | 数组+链表+红黑树 |
    | 是否可以为null | Key和value都不能为null | 可以为null |
    | hash算法 | key的hashCode() | 二次hash |
    | 扩容方式 | 当前容量翻倍 +1 | 当前容量翻倍 |
    | 线程安全 | 同步(synchronized)的,线程安全 | 非线程安全 |

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

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

相关文章

利用GPT4o Captcha工具和AI技术全面识别验证码

利用GPT4o Captcha工具和AI技术全面识别验证码 &#x1f9e0;&#x1f680; 摘要 GPT4o Captcha工具是一款命令行工具&#xff0c;通过Python和Selenium测试各种类型的验证码&#xff0c;包括拼图、文本、复杂文本和reCAPTCHA&#xff0c;并使用OpenAI GPT-4帮助解决验证码问…

yolov5-7在opencv里跑自己的onnx模型

先把模型放在如下目录 运行如下代码 import cv2 import numpy as npclass Onnx_clf:def __init__(self, onnx:strdnn_model1/plane02.onnx, img_size640, classlist:list[plane]) -> None: func: 读取onnx模型,并进行目标识别para onnx:模型路径img_size:输出图片大小,和模…

Codeforces Round 962 (Div. 3) D. Fun (数学,暴力枚举)

非常好题目&#xff0c;使我思考良多。 经典的数学问题&#xff0c;题目给出了两道数学式子&#xff0c;当然就是要让我们推啊。 首先纠正一个错误&#xff0c;当我发现了这两个式子能凑出来平方式的时候我就想着去用两个式子来互相简化&#xff0c;但其实这样存在以下一个错误…

数据增强和数据平衡

数据增强&#xff08;Data Augmentation&#xff09; 数据增强是一种通过对数据进行变换来增加数据多样性的方法。它可以提高模型的泛化能力&#xff0c;特别是在图像和文本处理任务中。 原理 通过数据增强技术&#xff0c;生成更多样本&#xff0c;提高模型的泛化能力。例如…

Github个人网站搭建详细教程【Github+Jekyll模板】

文章目录 前言一、介绍1 Github Pages是什么2 静态网站生成工具3 Jekyll简介Jekyll 和 GitHub 的关系 4 Mac系统Jekyll的安装及使用安装Jekyll的简单使用 二、快速搭建第一个Github Pages网站三、静态网站模板——Chirpy1 个人定制 四、WordPress迁移到Github参考资料 前言 23…

chrome浏览器驱动(所有版本)

chrome浏览器驱动 114之前版本 https://chromedriver.storage.googleapis.com/index.html 125以后 125以后版本下载链接在此&#xff0c;只有后面status是绿色对勾的才可以下载&#xff0c;驱动大版本一致就可以使用&#xff0c;不需版本号一模一样&#xff1b;下载所需版本只…

STM32智能工业监控系统教程

目录 引言环境准备智能工业监控系统基础代码实现&#xff1a;实现智能工业监控系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;工业监控与优化问题解决方案与优化收尾与总结 1. 引言 智能工业监控系统通…

Java集合框架2024最通俗易懂(图片超全)

集合 1.1、定义 集合就是类型统一的数据组合而成的数据结构&#xff0c;该数据结构可以任意的改变长度。 1.3、Set Set数据存储结构&#xff0c;无序&#xff0c;且不可以重复&#xff0c;元素可以为null&#xff0c;但是也只能出现一次&#xff0c;如下图: 1.3.1、HashSe…

2. 卷积神经网络无法绕开的神——LeNet

卷积神经网络无法绕开的大神——LeNet 1. 基本架构2. LeNet 53. LeNet 5 代码 1. 基本架构 特征抽取模块可学习的分类器模块 2. LeNet 5 LeNet 5: 5 表示的是5个核心层&#xff0c;2个卷积层&#xff0c;3个全连接层.核心权重层&#xff1a;卷积层、全连接层、循环层&#xff…

从零开始学习网络安全渗透测试之基础入门篇——(二)Web架构前后端分离站Docker容器站OSS存储负载均衡CDN加速反向代理WAF防护

Web架构 Web架构是指构建和管理Web应用程序的方法和模式。随着技术的发展&#xff0c;Web架构也在不断演进。当前&#xff0c;最常用的Web架构包括以下几种&#xff1a; 单页面应用&#xff08;SPA&#xff09;&#xff1a; 特点&#xff1a;所有用户界面逻辑和数据处理都包含…

Apache Nifi挂接MQTT与Kafka实践

目录 1. 说明&#xff1a; 2. 方案设计&#xff1a; 2.1 资源配置&#xff1a; 2.2 交互Topics: 3. 实现步骤 3.1 Nifi 桌面 3.2 MqttToKafka 3.2.1 配置 3.2.2 测试 3.2.3 结果 3.3 KafkaToMqtt 3.3.1 配置 3.3.1 测试 3.3.1 结果 ​编辑 4. 总结&#xff…

web学习笔记(八十三)git

目录 1.Git的基本概念 2.gitee常用的命令 3.解决两个人操作不同文件造成的冲突 4.解决两个人操作同一个文件造成的冲突 1.Git的基本概念 git是一种管理代码的方式&#xff0c;广泛用于软件开发和版本管理。我们通常使用gitee&#xff08;码云&#xff09;来云管理代码。 …

使用SpringTask框架

目录 一.什么是SpringTask&#xff1f; 二.cron表达式&#xff1a; 三.SpringTask框架的使用操作&#xff1a; 1.导入maven坐标spring-context&#xff1a; 2.启动类添加 EnableScheduling 以此来开启任务调度&#xff1a; 3.自定义定时任务类&#xff1a; 普通案例&#…

如何写好技术文档 - 来自Google十多年的文档经验

[导读]本文大部分内容翻译总结自《Software Engineering at Google》第10章节 Documentation。另外&#xff0c;该书电子版近日已经可以免费阅读了https://qiangmzsx.github.io/Software-Engineering-at-Google/#/?idsoftware-engineering-at-google&#xff0c;有兴趣的同学可…

Dockerfile指令详解和Docker操作命令

1.容器的特点&#xff1a;1&#xff09;自包含&#xff08;包括应用程序及其运行环境&#xff09;&#xff1b;2&#xff09;可移植&#xff1b;3&#xff09;相互隔离&#xff1b;4&#xff09;轻量级。 2.docker成为容器的事实标准在于&#xff1a;1&#xff09;在运行环境上…

Unity 资源 之 Pop It 3D 解压玩具与双人AI游戏 Unity 资源包分享

精彩呈现&#xff1a;Pop It 3D 解压玩具与双人AI游戏 Unity 资源包分享 一、Pop It 3D 解压玩具的魅力二、双人游戏的互动乐趣三、Unity 游戏资源包的优势四、如何获取资源包 亲爱的游戏爱好者们&#xff0c;今天为大家带来一款令人兴奋的游戏资源——Pop It 3D 解压玩具双人带…

ubuntu串口重命名助手arm64架构(下)Qt交叉编译arm64

✨✨ Rqtz 个人主页 : 点击✨✨ &#x1f388;PyQt系列专栏:点击&#x1f388; &#x1f388;Qt智能车上位机专栏: 点击&#x1f388; &#x1f388;Qt串口助手专栏:点击&#x1f388; &#x1f4ab;宗旨:共享IT之美,共创机器未来 目录 前言 设备介绍 查询系统架构 下载…

【JVM基础05】——组成-能不能解释一下方法区?

目录 1- 引言&#xff1a;方法区概述1-1 方法区是什么&#xff1f;(What)1-2 为什么用方法区&#xff1f;方法区的作用 (Why) 2- ⭐核心&#xff1a;详解方法区(How)2-1 能不能解释一下方法区&#xff1f;2-2 元空间内存溢出问题2-3 什么是常量池&#xff1f;2-4 运行时常量池 …

Stable Diffusion WebUI本地环境搭建

一、项目代码下载 git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui 二、环境配置 conda create --n stafu python3.10.6 实际上跟自己创建的环境没有关系&#xff0c;项目启动会自动复制这个环境&#xff0c;之后项目根据这个基础环境构建 也可以在自己…

【五】MySql8基于m2芯片arm架构Ubuntu24虚拟机安装

文章目录 1. 更新系统包列表2. 安装 MySQL APT Repository3. 更新系统包列表4. 安装 MySQL Server5. 运行安全安装脚本6. 验证 MySQL 安装7. 配置远程连接7.1 首先要确认 MySQL 配置允许远程连接&#xff1a;7.2 重启 MySQL 服务&#xff1a;7.3 检查 MySQL 用户权限&#xff1…