01-Java集合之单向队列,如Collection接口,List接口,Set接口,Queue接口及其实现类的底层结构和特点

news2024/11/24 14:09:27

单列集合

特点

单列集合分为三大类

  • List类型的集合: 有序可重复 , 这种类型的集合的元素都有下标
  • Set类型的集合: 无序不可重复 , 这种类型的集合的元素都没有下标
  • Queue类型的集合: 先进先出(FIFO) , 只能一端进并且在另一端出的队列

Collection中能存放的元素: 没有使用泛型之前Collection中可以存储Object的所有子类型,使用了泛型之后Collection中只能存储某个指定的类型

  • 存放在List集合中的元素一定要重写equals方法, 因为调用集合的 contains和remove方法时会调用对象的equals方法判断两个对象是否相等
  • 存放在Set集合中的元素需要同时重写hashCode()+equals()方法,因为是先调用hashCode()方法确定元素所在分区,后调用equals()方法与该分区元素比较

单列集合继承结构图

在这里插入图片描述

Collection父接口

向集合中添加/删除元素的方法

方法名功能
boolean add(Object e)向集合中添加元素(默认都是向集合末尾添加元素) , 没有使用泛型之前Collection中可以存储Object的所有子类型
boolean addAll(Collection c)向集合中添加多个元素即添加一个集合
boolean remove(Object o)删除集合中的某个元素,也可以通过下标指定删除
boolean removeAll(Collection c)删除集合中的多个元素即删除一个集合
Object[] toArray()把集合转换成数组 , 同理Arrays工具类中也有方法把数组转化成集合

对集合中元素的判断

方法名功能
int size()获取集合中元素的个数
void clear()清空集合的元素
boolean contains(Object o)判断当前集合中是否包含某个元素,底层调用该元素的equals方法与集合中的元素一个一个进行比对
boolean containsAll(Collection c)判断集合中是否包含多个元素即一个集合
boolean isEmpty()判断该集合中元素的个数是否为0
public class CollectionTest01 {
    public static void main(String[] args) {
        // 利用多态创建一个集合对象,接口是抽象的无法实例化
        Collection c = new ArrayList();
        
        // 测试add方法向集合中添加元素 
        // 自动装箱(java5的新特性),实际上是放进去了一个对象的内存地址
        c.add(1200); 
        c.add(3.14); 
        c.add(new Object());
        c.add(new Student());
        c.add(true); 

        // 测试size方法,获取集合中元素的个数
        System.out.println("集合中元素个数是:" + c.size()); // 5

        // 测试clear方法清空集合中的元素
        c.clear();
        System.out.println("集合中元素个数是:" + c.size()); // 0

        // 再次向集合中添加元素
        c.add("绿巨人");

        // 测试contains方法判断集合中是否包含"绿巨人"
        boolean flag = c.contains("绿巨人");
        System.out.println(flag); // true
        boolean flag2 = c.contains("蓝巨人");
        System.out.println(flag2); // false

        // 测试remove方法删除集合中某个元素
        c.remove("绿巨人");
        System.out.println("集合中元素个数是:" + c.size()); // 0

        // 判断集合是否为空
        System.out.println(c.isEmpty()); // true
       
        // 再向集合中添加元素
        // "helloworld!"对象的内存地址放到了集合当中
        c.add("helloworld!");
        c.add(new Student());

        // 测试toArray()方法将集合转换成数组
        Object[] objs = c.toArray();
        for(int i = 0; i < objs.length; i++){
            // 遍历数组输出集合中的元素
            Object o = objs[i];
            System.out.println(o);
        }
    }
}

class Student{
}

深入contains和remove方法

contains方法原理: 当需要判断集合中是否包含某个元素时,底层是拿着该元素并调用它的equals方法与集合中的元素一个一个进行比对判断是否相等,相等则包含

在这里插入图片描述

public class CollectionTest04 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c = new ArrayList();

        // 向集合中存储元素
        String s1 = new String("abc"); // s1 = 0x99
        c.add(s1); 

        String s2 = new String("def"); // s2 = 0x77
        c.add(s2);

        // 新建的对象String
        String x = new String("abc"); // x = 0x94
        
        // 虽然c集合不包含x对象的内存地址,但是调用equals方法时会判断它和s1相等,等价于集合中包含x
        System.out.println(c.contains(x)); //true
    }
}

remove方法原理: 当需要删除集合中对应的元素时,底层是拿着该元素并调用它的equals方法与集合中的元素一个一个进行比对判断是否相等,相等则删除

public class CollectionTest05 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c = new ArrayList();
        // 创建用户对象
        User u1 = new User("jack");
        // 将u1对象加入集合
        c.add(u1);
        // 创建u2对象
        User u2 = new User("jack");
        
        // 删除集合中的u2对象
        // User类没有重写equals之前不会删除集合中的u1, 重写equals之后此时u1和u2是等价的所以会删除u1
        c.remove(u2);
        // 元素个数0个
        System.out.println(c.size()); 
    }
}

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

    // 重写equals方法,比较原理是只要姓名一样就表示同一个用户
    public boolean equals(Object o) {
        if(o == null || !(o instanceof User)) return false;
        if(o == this) return true;
        User u = (User)o;
        return u.name.equals(this.name);
    }
}

Iterable/foreach遍历集合

通用方式: 先获取集合对象的迭代器对象Iterator,通过获取的迭代器对象开始迭代/遍历集合,foreach底层对集合或数组的遍历也是利用的迭代器原理

集合获取Iterable迭代器对象的方法

方法名功能
Iterator iterator()获取集合对应的Iterator 类型的迭代器对象,对集合中的元素进行迭代

Iterable迭代器对象的方法

方法名功能
boolean hasNext()判断集合中是否还有元素可以迭代,最初迭代器没有指向任何元素
Object next()返回迭代器指向的元素
在没有指定泛型之前 , 无论存进去什么的类型元素取出来元素的编译类型统一都是 Object 类型的,但是元素的运行类型没有改变
获取元素前一定要先判断集合中是否还有元素, 如果没有元素可以迭代则会出现java.util.NoSuchElementException异常
void remove(o)删除迭代器指向的元素,此时会自动更新迭代器和更新集合中的元素
直接通过集合去删除元素不会更新迭代器, 就会导致迭代器参照的快照和集合中的元素状态不同

当集合的结构发生改变时必须重新获取迭代器对象, 如果还是用以前老的迭代器会出现java.util.ConcurrentModificationException异常

  • 当我们获取迭代器对象的时候,迭代器会对当前集合的状态拍一个快照,迭代器对象会参照这个快照对集合进行迭代
  • 迭代器对象每迭代一次后都会拿快照与集合中的元素状态进行比较,如果不一致java.util.ConcurrentModificationException异常

使用集合的remove方法或迭代器的remove方法删除集合元素的区别

  • 集合的remove方法: 直接删除集合中的元素并不会通知迭代器更新快照即迭代器不知道集合元素状态变化了

  • 迭代器的remove方法: 删除集合中的元素并且会通知迭代器更新快照

在这里插入图片描述

public class CollectionTest06 {
    public static void main(String[] args) {
        Collection c = new ArrayList();
		// 创建集合
        Collection c = new ArrayList();
        // 此时获取的迭代器是集合中没有元素状态下的迭代器
        Iterator it = c.iterator();

		// 向集合添加中元素,集合的状态发生了改变
        c.add("abc");
        c.add("def");
        c.add("xyz");

		// 重新获取迭代器,此时获取的是集合中有3个元素状态下的迭代器
        Iterator it2 = c2.iterator();
        while(it2.hasNext()){
            Object o = it2.next();
            // 迭代器的remove方法删除迭代器指向的当前元素
            it2.remove(); 
            System.out.println(o);
        }

        System.out.println(c2.size()); //0
    }
}

List接口的实现类

List类型的集合存储元素特点就是有序可重复

  • 有序: 元素有下标并且从0开始以1递增, 因为有下标所以List集合可以通过下标遍历集合
  • 可重复:可以重复存储同一个元素
  • 元素特点: 存放在List集合中的元素一定要重写equals方法, 因为调用集合的 contains和remove方法时会调用对象的equals方法判断两个对象是否相等

List接口的常用方法

List是Collection接口的子接口有自己特有的方法

方法名功能
void add(int index, Object element)在集合中的指定下标位置插入指定一个元素 , 不指定下标默认是向集合末尾添加元素
void addAll(int index, Collection c)在集合中的指定下标位置插入一个集合的元素, 不指定下标默认是向集合末尾添加元素
Object remove(int index)删除集合中指定下标位置的元素,对于ArrayList集合来说效率比较低
Object set(int index, Object element)将集合中指定下标位置的元素修改/替换为另一个对象
Object get(int index)根据下标获取集合中的元素
int indexOf(Object o)获取指定对象在集合中第一次出现处的索引
int lastIndexOf(Object o)获取指定对象在集合中最后一次出现处的索引
List subList(int fromIndex , int toIndex)返回从fromIndex到toIndex位置的一个新集合(前闭后开)
public class ListTest01 {
    public static void main(String[] args) {
        // 创建任意一种List类型的集合
        //List myList = new LinkedList();
        //List myList = new Vector();
        List myList = new ArrayList();

        // 默认都是向集合末尾添加元素
        myList.add("A"); 
        myList.add("B");
        myList.add("C");

        // 在集合中的指定下标位置插入指定元素,对于ArrayList集合来说效率比较低
        myList.add(1, "C");

        // 根据下标获取集合中的元素
        Object firstObj = myList.get(0);
        System.out.println(firstObj);// A

        // 通过下标遍历集合
        // A C B C
        for(int i = 0; i < myList.size(); i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }

        // 获取指定对象在集合中第一次出现处的索引
        System.out.println(myList.indexOf("C")); // 1

        // 获取指定对象在集合中最后一次出现处的索引
        System.out.println(myList.lastIndexOf("C")); // 3

        // 删除集合中指定下标位置的元素,对于ArrayList集合来说效率比较低
        myList.remove(0);
        System.out.println(myList.size()); // 3

        // 修改/替换集合中指定下标位置的元素
        myList.set(2, "D");

        // 通过下标遍历集合
        // C B D
        for(int i = 0; i < myList.size(); i++){
            Object obj = myList.get(i);
            System.out.println(obj);
        }
    }
}

ArrayList集合(数组)

ArrayList集合没有指定泛型前底层是一个Object[]数组,执行List接口通过指定下标位置元素进行添加或删除的方法时效率较低,因为涉及到数组中元素的位移

  • 集合扩容: 增长到原容量的1.5倍, 数组扩容效率比较低建议在创建ArrayList集合的时候给定一个初始化容量
  • 检索效率最高: 数组的每个元素占用空间大小相同且内存地址是连续的, 可以通过首元素内存地址和指定位置的下标快速定位到指定元素的内存地址
  • 随机增删元素效率比较低: 向数组末尾添加元素效率很高,集合中可以添加多个null
  • 数据存储: 无法存储大数据量的数据,因为很难找到一块非常巨大的连续的内存空间
  • 安全: 非线程安全
构造方法功能
ArrayList()默认数组的初始化容量为10,底层是先创建了一个长度为0的数组,只有当添加了第一个元素时才会初始化数组容量为10
ArrayList(int)指定数组的初始化容量
ArrayList(Collection)将Collection类型的集合转换成ArrayList类型的集合,这样就可以使用List集合特有的遍历方式
public class ArrayListTest02 {
    public static void main(String[] args) {
        // 默认数组的初始化容量为10
        List myList1 = new ArrayList();

        // 指定数组的初始化容量为100
        List myList2 = new ArrayList(100);

        // 创建一个HashSet集合
        Collection c = new HashSet();
        // 添加元素到Set集合
        c.add(100);
        c.add(200);
        c.add(900);
        c.add(50);

        // 将HashSet集合转换成List集合
        List myList3 = new ArrayList(c);
        for(int i = 0; i < myList3.size(); i++){
            System.out.println(myList3.get(i));
        }
    }
}

Vector集合(数组)

Vector集合相对于ArrayList集合效率较低所以使用较少,因为我们可以使用集合工具类Collections将一个线程不安全的ArrayList集合转换成线程安全的

  • 集合扩容: Vector集合的底层数组每次扩容之后是原容量的2倍

  • 安全: 线程安全,所有方法都带有synchronized关键字即是线程同步的,但是由于执行效率太低已被弃用

public class VectorTest {
    public static void main(String[] args) {
        // 创建一个Vector集合
        List vector = new Vector();
        //Vector vector = new Vector();

        // 添加元素 , 默认初始化容量10个。满了之后扩容(扩容之后的容量是20)
        vector.add(1);

        Iterator it = vector.iterator();
        while(it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }

        // 将一个线程不安全的ArrayList集合转换成线程安全的
        List myList = new ArrayList(); 

        // 变成线程安全的
        Collections.synchronizedList(myList); 

        // myList集合就是线程安全的了。
        myList.add("111");
    }
}

Stack集合

Stack类继承并扩展了Vector类,允许将一个向量作为堆栈处理用来表示对象的后进先出(LIFO)

  • 推荐使用Deque替代Stack: Stack类中同时暴露了set/get方法, 即可以随机访问Stack类中存储的任意一个元素, 这与Stack先进后出的设计理念相冲突
方法作用
push(E)把元素压栈
pop(E)把栈顶的元素“弹出”
peek(E)获取栈顶元素但不弹出
boolean empty()判断栈是否为空
int search(Object o)查找某个元素在栈中的位置(距离栈顶有多远)
Stack s = new Stack();
s.push(1);
s.pop();
System.out.println(s);

LinkedList(双向链表)

LinkedList集合底层是双向链表并且集合中的元素也有下标

  • 集合扩容: 链表没有初始化容量, 最初链表中没有任何元素即头节点的first和last引用的值都是null
  • 随机增删效率较高: 链表上的元素在空间存储上内存地址不连续,所以随机增删元素的时候不会有大量元素位移
  • 检索/查找效率较低: 查找某个元素时只能从头节点开始一个一个遍历直到找到为止
  • 应用场景: 栈、队列或双端队列
public class LinkedListTest01 {
    public static void main(String[] args) {
        // LinkedList集合底层也是有下标的
        List list = new LinkedList();
        list.add("a");
        list.add("b");
        list.add("c");

        for(int i = 0; i <list.size(); i++){
            Object obj = list.get(i);
            System.out.println(obj);
        }
    }
}

LinkedList底层添加元素源码分析

在这里插入图片描述

public boolean add(E e) {
    linkLast(e);
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

Set接口的实现类

Set集合存储元素的特点无序不可重复,往Set集合中存放的数据时等价于放到了Map集合的key部分(本质就是个特殊的Map集合)

  • 无序 : Set集合中的元素没有下标, 存进去和取出的顺序不一定相同
  • 不可重复:同一个元素添加第二次时虽然没有报错 ,但不会再次存储到集合中
  • 元素特点: 存放在Set/Map集合中的元素需同时重写hashCode()+equals()方法,先调用hashCode()方法确定元素所在分区,后调用equals()方法与该分区元素比较

HashSet集合(HashMap)

测试Set集合中常用方法

public class HashSetTest01 {
    public static void main(String[] args) {
        // HashSet集合特点: 无序不可重复
        Set<String> strs = new HashSet<>();

        // 添加元素
        strs.add("hello3");
        strs.add("hello1");
        // 存不进去
        strs.add("hello3");
        
        //迭代器遍历
        Iterator strs = strs.iterator();
        while(strs.hasNext()){
            System.out.println(strs.next());
        }

        // 增强for遍历
        for(String s : strs){
            System.out.println(s);
        }
    }
}

TreeSet集合(TreeMap)

由于TreeSet集合实现了SortedSet接口,所以集合中的元素默认按照字典顺序规则升序排序,也可以通过两种方式手动指定排序规则

  • 第一种方式:存储到集合中的元素需实现Comparable接口,然后重写 compareTo方法(返回值决定了元素的排序规则),重写了该方法后就无需重写equals方法
  • 第二种方式: 构造TreeSet集合时传入一个实现了Comparator接口的比较器对象并实现compare方法,该比较器对象可以对集合中的元素进行挨个比较
public class TreeSetTest02 {
    public static void main(String[] args) {
        // 创建一个TreeSet集合
        TreeSet<String> ts = new TreeSet<>();
        
        // 添加String
        ts.add("zhangsan");
        ts.add("lisi");
        // 遍历
        for(String s : ts){
            // 默认按照字典顺序升序
            System.out.println(s);
        }
        
		// 创建一个TreeSet集合
        TreeSet<Integer> ts2 = new TreeSet<>();
        // 添加Integer,底层自动类型转换
        ts2.add(100);
        ts2.add(200);
        ts2.add(10);
        for(Integer elt : ts2){
            // 默认升序
            System.out.println(elt);
        }
    }
}

Queue接口的实现类

Queue是Collection接口的子接口,它的特点是先进先出(FIFO), 底层是一个只能一端进并且在另一端出的单向队列

在这里插入图片描述

除了基本的集合操作之外,Queue接口额外提供了插入,提取和检查的方法,方法以两种形式存在即可以抛出异常或者返回特殊值

抛出异常的方法返回特殊值的方法功能
add(e)offer(e)插入元素失败返回false插入
remove(e)poll()取出失败返回null取出
element(e)peek()该元素不存在返回null检查

Deuqe(double ended queue)

Deuqe继承自Queue接口是Java中的双端队列集合类型,在队列的两端都能插入和删除元素,既具备普通队列FIFO(先进先出)的功能也具备Stack的LIFO(后进先出)功能

  • 模拟队列:元素添加和删除的位置是双端队列的不同位置
  • 模拟堆栈: 元素添加和删除的位置是双端队列的相同位置
// 普通队列只能一端进另一端出 
Queue queue = new LinkedList()Deque deque = new LinkedList();
// 双端队列两端都可以进出
Deque deque = new LinkedList();
// 堆栈
Deque deque = new LinkedList();

Deuqe接口不仅继承了父接口Queue的方法,自己也扩展了方法

抛出异常的方法返回特殊值的方法功能
addFirst(e)offerFirst(e)插入元素失败返回false将指定的元素插入此双端队列的前面
removeFirst(e)pollFirst()取出失败返回null取出第一个元素
getFirst(e)peekFirst()该元素不存在返回null检查第一个元素
抛出异常的方法返回特殊值的方法功能
addLast(e)offerLast(e)插入元素失败返回false将指定的元素插入此双端队列的后面
removeLast(e)pollLast()取出失败返回null取出最后一个元素
getLast(e)peekLast()该元素不存在返回null检查最后一个元素

Deque接口模拟栈的扩展方法: 不要直接调用addFirst()/removeFirst()/peekFirst()方法, 那样会破坏栈的本质

方法功能
push(e)把元素压栈,从头不添加
pop(e)把栈顶的元素弹出,从头部删除
peek(e)取栈顶元素但不弹出, 从头部取出
public class Main {
    public static void main(String[] args) {
    // 默认构造器
    Deque<String> deque = new LinkedList<>();
    // 从头部开始添加元素,先添加的先到尾部
    // 此时deque中元素的位置(从头部开始):Java, Like , I
    deque.addFirst("I");
    deque.addFirst("Like");
    deque.addFirst("Java");
 
    System.out.println("---------get-------");
    // 获取头
    System.out.println(deque.getFirst()); // Java 
    // 获取尾
    System.out.println(deque.getLast());  // I     
    System.out.println("--------poll-------");
    // 弹出栈顶元素
    System.out.println(deque.poll());    
    System.out.println("--------push-------");
    // 从栈顶开始添加元素
    deque.push("LanQiao");           
    System.out.println(deque.pollFirst());// LanQiao
    System.out.println(deque.pollLast()); // I
    deque.addFirst("LZP");
    System.out.println("--------peek--------");
    // 等价于peekFirst()取出队头元素,但是队列中还是存在该元素
    System.out.println(deque.peek()); 
    System.out.println(deque.peekFirst());
    System.out.println(deque.peekLast());
    System.out.println(deque.size()); // 2
    System.out.println("=======real remove=========");
    // poll才是真正的remove
    System.out.println(deque.poll()); 
    System.out.println(deque.size());// 1
    }
}

ArrayDeque(数组结构)

ArrayDeque的底层是数组,不支持插入为null的元素

  • 容量: 没有容量限制,可根据需求自动进行扩容但会影响效率
  • 应用场景: 作为栈使用时效率要高于Stack,作为队列来使用效率相较于LinkedList要更好一些

PriorityQueue(数组结构)

PriorityQueue实现了Queue接口,优先队列中的元素默认按元素的比较器方法自然排序,或者通过队列构造时提供的Comparator比较器在队列实例化的时排序

  • 元素特点: 按照每层从左到右的顺序存放,不允许存入null值且不支持存储不可比较的对象
  • 优先队列的头元素: 当我们获取队列时返回的是队列的头对象, 头元素是基于自然排序或者Comparator排序的最小元素
  • 容量: 容量大小不受限制,在创建时可以也指定初始大小,当我们向优先队列增加元素的时候,队列大小会自动增加
  • 安全: 非线程安全的,Java提供了实现BlockingQueue接口的PriorityBlockingQueue用于Java多线程环境
构造函数描述
PriorityQueue()使用默认的容量(11)创建一个优先队列 , 元素的顺序规则采用的是自然顺序 (看元素是否实现比较接口)
PriorityQueue(int 容量)使用指定的容量创建一个优先队列 , 元素的顺序规则采用的是自然顺序
PriorityQueue(比较器)使用默认的容量创建一个优先队列 , 元素的顺序规则采用的是比较器
PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});

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

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

相关文章

国家万亿资金助力城市生命线城市内涝积水监测系统

自2023年年初以来&#xff0c;我国多个地区遭遇了洪涝、干旱、台风、风雹等灾害的侵袭&#xff0c;部分地区灾情严重&#xff0c;经济损失较大。为应对灾后恢复重建工作的艰巨任务&#xff0c;本次国债将主要投向灾后恢复重建以及提升防灾减灾救灾能力。其中&#xff0c;将全面…

统信UOS安装Virtualbox虚拟机和Windows10系统

在UOS统信系统中部署Windows环境我可以通过安装虚拟机来实现&#xff0c;这也可以解决软件不适配带来的一些问题&#xff0c;当然对硬件配置也有一定的要求&#xff0c;不建议性能过低的设备使用。 接下来请按照以下步骤进行安装Virtualbox及Win10虚拟系统的设置。 1、安装Vi…

docker容器的生命周期管理常用命令

容器的生命周期管理命令 docker create &#xff1a;创建一个新的容器但不启动它 docker create nginx docker run :创建一个新的容器并运行一个命令 常用选项&#xff1a; 常用选项1. --add-host&#xff1a;容器中hosts文件添加 host:ip 映射记录 2. -a, --attach&#…

【论文阅读】ActiveNeRF:通过不确定性估计候选新视图

【论文阅读】ActiveNeRF: Learning where to See with Uncertainty Estimation Abstract1 Introduction3 Background4 NeRF with Uncertainty Estimation5 ActiveNeRF5.1 Prior and Posterior Distribution5.2 Acquisition Function5.3 Optimization and Inference 6 Experimen…

Redis面试题:Redis是单线程的,但是为什么还那么快?I/O多路复用模型

目录 面试官&#xff1a;Redis是单线程的&#xff0c;但是为什么还那么快&#xff1f; 面试官&#xff1a;能解释一下I/O多路复用模型&#xff1f; 面试官&#xff1a;Redis是单线程的&#xff0c;但是为什么还那么快&#xff1f; 候选人&#xff1a; 嗯&#xff0c;这个有几…

Robot Framework自动化测试(四)--- 分层思想

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

【广州华锐互动】节约用水VR互动教育:身临其境体验水资源的珍贵!

随着技术的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术在许多领域得到了广泛应用。在节水宣传教育方面&#xff0c;VR技术也展现出了其独特的优势。与传统宣传教育方式相比&#xff0c;节约用水VR互动教育具有更加沉浸式、互动性和实践性的特点&#xff0c;能…

Java计算二叉树从根节点到叶子结点的最大路径和

要求从根节点到叶子结点的最大路径和&#xff0c;可以通过递归遍历二叉树来实现。对于二叉树中的每个节点&#xff0c;我们都可以考虑包含该节点的最大路径和。在递归的过程中&#xff0c;我们需要不断更新全局最大路径和。 具体的思路如下&#xff1a; 递归函数设计&#xff1…

【Python】python包相对导入问题及解决方案

报错信息 ImportError: attempted relative import with no known parent package 问题描述 在package目录下有a1.py文件和subpackage1目录。在subpackage1目录下有b1.py文件。现在b1.py文件中有这样一行代码&#xff1a;from …a1 import A1。在subpackage1目录下运行pytho…

Windows公网远程连接MongoDB数据库【无公网IP】

目录 前言 1. 安装数据库 2. 内网穿透 2.1 安装cpolar内网穿透 2.2 创建隧道映射 2.3 测试随机公网地址远程连接 3. 配置固定TCP端口地址 3.1 保留一个固定的公网TCP端口地址 3.2 配置固定公网TCP端口地址 3.3 测试固定地址公网远程访问 总结 前言 MongoDB是一个基…

案例033:基于微信小程序的商品展示系统设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

paddleocr笔记

PP-OCRv1 PP-OCR中&#xff0c;对于一张图像&#xff0c;需要完成以下3个步骤提取其中的文字信息&#xff1a; 使用文本检测方法&#xff0c;获取文本区域多边形信息&#xff08;PP-OCR中文本检测使用的是DBNet&#xff0c;因此获取的是四点信息&#xff09;。对上述文本多边形…

SpringBoot参数校验@Validated和@Valid的使用

1、Validated和Valid区别 Validated&#xff1a;可以用在类、方法和方法参数上。但是不能用在成员属性&#xff08;字段&#xff09;上Valid&#xff1a;可以用在方法、构造函数、方法参数和成员属性&#xff08;字段&#xff09;上 2、引入依赖 Spring Boot 2.3 1 之前&…

【Linux系统编程】操作系统详解(什么是操作系统?为什么会存在操作系统?设计操作系统的目的是什么?)

目录 一、前言 二、 什么是操作系统 &#x1f4a6;操作系统的引入 &#x1f4a6;操作系统的概念理解 &#x1f4a6;操作系统设计的目的与定位 &#x1f4a6;总结 二、操作系统之上之下分别有什么 三、深度理解操作系统的“管理” &#x1f4a6;场景理解 &#x1f4a6;操…

使用thymeleaf模板生成静态文件demo

一&#xff1a;pom.xml添加依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> <dependency><groupId>ognl</groupId><artifactId…

NJU操作系统公开课笔记(2)

上期目录&#xff1a; NJU操作系统公开课笔记&#xff08;1&#xff09;https://blog.csdn.net/jsl123x/article/details/134431343?spm1001.2014.3001.5501 目录 一.处理器与寄存器 二.中断 三.中断系统 四.进程 五.线程与多线程技术概述 六.处理器调度算法 一.处理器…

贪吃蛇小游戏基本简单布局

代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>Layui贪吃蛇小游戏</title> <link rel"stylesheet" href"https://cdn.bootcdn.net/ajax/libs/layui/2.5.7/css/layui.…

【数据库】执行计划中的两趟算法机制原理,基于排序算法来分析,算法的限制,执行代价以及优化

基于排序的两趟算法 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定…

跨越速运在货运高峰的同时,受邀参加国际医疗器械博览会

作为国内领先的物流企业&#xff0c;跨越速运在一年一度的年终大促&#xff0c;货运高峰的同时&#xff0c;受第88届中国国际医疗器械博览会&#xff08;以下简称CMEF&#xff09;活动主办方邀请&#xff0c;&#xff09;于2023年10月31日&#xff0c;在深圳国际会展中心重磅亮…

Linux驱动开发——网络设备驱动(理论篇)

目录 一、前言 二、网络层次结构 三、网络设备驱动核心数据结构和函数 一、前言 网络设备驱动是 Linux 的第三大类驱动&#xff0c;也是我们学习的最后一类 Linux 驱动。这里我们首先简单学习一下网络协议层次结构&#xff0c;然后简单讨论 Linux 内核中网络实现的层次结构。…