【数据结构】LinkedList ------ java实现

news2024/11/15 14:10:49

  知识框架图:

  LinkedList是一种常用的数据结构。底层是一个双向链表。每个节点包含数据以及指向前一个节点和后一个节点的引用。

   一:LinkedList的使用

   1.1 LinkedList的构造方法

方法 解释
LinkedList() 无参构造
public LinkedList(Collection<? extends E> c) 使用其他集合容器中元素构造List

  代码示例: 

public static void main(String[] args) {
// 构造一个空的LinkedList
List<Integer> list1 = new LinkedList<>();
List<String> list2 = new ArrayList<>();

list2.add("三国演义");
list2.add("西游记");
list2.add("水浒传");

// 使用ArrayList构造LinkedList
List<String> list3 = new LinkedList<>(list2);
}

  1.2 LinkedList的常用方法

方法 解释
boolean add (E e) 尾插 e
void add (int index, E element)将 e 插入到 index 位置
boolean addAll (Collection<? extends E> c)尾插 c 中的元素
E remove (int index) 删除 index 位置元素
boolean remove (Object o)删除遇到的第一个 o
E get (int index)获取下标 index 位置元素
E set (int index, E element)将下标 index 位置元素设置为 element
void clear () 清空
boolean contains (Object o) 判断 o 是否在线性表中
int indexOf (Object o) 返回第一个 o 所在下标
int lastIndexOf (Object o)返回最后一个 o 的下标
List<E> subList (int fromIndex, int toIndex)截取部分 list

  部分方法代码示例:

public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1); // add(elem): 表示尾插
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
System.out.println(list.size());
System.out.println(list);
// 在起始位置插入0
list.add(0, 0); // add(index, elem): 在index位置插入元素elem
System.out.println(list);
list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
list.removeFirst(); // removeFirst(): 删除第一个元素
list.removeLast(); // removeLast(): 删除最后元素
list.remove(1); // remove(index): 删除index位置的元素
System.out.println(list);
// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
if(!list.contains(1)){
list.add(0, 1);
}

list.add(1);
System.out.println(list);
System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
int elem = list.get(0); // get(index): 获取指定位置元素
list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
System.out.println(list);
// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
List<Integer> copy = list.subList(0, 3); 
System.out.println(list);
System.out.println(copy);
list.clear(); // 将list中元素清空
System.out.println(list.size());
}

  1.3 LinkedList的遍历 

  1.使用普通 for 循环遍历

//由于LinkedList不能像数组那样通过索引直接快速访问元素,使用普通 for 循环遍历需要借助size()方法和 
//get(int index)方法来逐个获取元素。这种方式效率较低,不推荐在频繁遍历的场景中使用。

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

  2.使用增强型 for 循环遍历

//增强for循环可以简洁地遍历集合中的元素。
for (String element : list) {
    System.out.println(element);
}

  3. 使用迭代器(Iterator)遍历

//1.使用迭代器的hasNext和next方法进行遍历,避免在遍历过程中频繁调用size方法
//或通过索引访问元素。这样可以提高遍历的效率,特别是在处理大型链表时。

//2.在遍历过程中尽量避免对链表进行修改操作,因为这可能会导致迭代器失效,需要重新获取迭代器。
//如果必须在遍历过程中进行修改,可以使用迭代器的remove方法来安全地删除元素。

LinkedList<String> list = new LinkedList<>();
list.add("计算机科学与技术");
list.add("软件工程");
list.add("人工智能");

//1.获取迭代器:可以通过LinkedList的iterator()方法获取一个Iterator对象。
Iterator<String> iterator = list.iterator();

//2.使用迭代器遍历:通过hasNext()方法判断是否还有下一个元素,使用next()方法获取下一个元素。
while (iterator.hasNext()) {
    String element = iterator.next();
    System.out.println(element);
}


  4. 从链表尾部向头部遍历


LinkedList<String> list = new LinkedList<>();
list.add("计算机科学与技术");
list.add("软件工程");
list.add("人工智能");


//1.获取列表迭代器并指向尾部:可以使用listIterator(int index)方法获取一个ListIterator对象,
//并将其初始位置设置为链表的尾部。
ListIterator<String> listIterator = list.listIterator(list.size());

//2.向头部遍历:使用hasPrevious()方法判断是否还有前一个元素,使用previous()方法获取前一个元素。
while (listIterator.hasPrevious()) {
    String element = listIterator.previous();
    System.out.println(element);
}

   二:使用案例

  2.1实现队列和栈数据结构

  1. 队列(Queue):

  • LinkedList 可以很方便地用来实现队列。在队列中,元素遵循先进先出(FIFO)的原则。可以利用 LinkedList 的 addLast 方法在队尾添加元素,使用 removeFirst 方法在队首取出元素。

  • 例如:在一个任务调度系统中,可以使用队列来管理待执行的任务。新任务添加到队尾,执行引擎从队首获取任务并执行。
 

  代码示例:

import java.util.LinkedList;

public class TaskQueue {
    private LinkedList<String> queue = new LinkedList<>();

    public void addTask(String task) {
        queue.addLast(task);
    }

    public String getNextTask() {
        return queue.removeFirst();
    }

    public static void main(String[] args) {
        TaskQueue taskQueue = new TaskQueue();
        taskQueue.addTask("Task 1");
        taskQueue.addTask("Task 2");
        System.out.println(taskQueue.getNextTask()); // 输出:Task 1
        System.out.println(taskQueue.getNextTask()); // 输出:Task 2
    }
}

  2. 栈(Stack):

  • LinkedList 也可以用于实现栈。在栈中,元素遵循先进后出(LIFO)的原则。可以使用 addFirst 方法将元素压入栈顶,使用 removeFirst 方法弹出栈顶元素。

  • 例如:在表达式求值、函数调用栈等场景中可以使用栈。
 

import java.util.LinkedList;

public class StackExample {
    private LinkedList<String> stack = new LinkedList<>();

    public void push(String item) {
        stack.addFirst(item);
    }

    public String pop() {
        return stack.removeFirst();
    }

    public static void main(String[] args) {
        StackExample stack = new StackExample();
        stack.push("Item 1");
        stack.push("Item 2");
        System.out.println(stack.pop()); // 输出:Item 2
        System.out.println(stack.pop()); // 输出:Item 1
    }
}

  2.2. 频繁插入和删除的场景

  1. 日志记录系统:

  • 在一个日志记录系统中,新的日志条目不断产生并需要添加到日志列表中。同时,可能需要根据时间范围或其他条件删除一些旧的日志条目。由于 LinkedList 在插入和删除操作上的高效性,特别是在列表中间进行插入和删除时,非常适合这种场景。

  • 代码示例:

import java.util.LinkedList;

public class LoggingSystem {
    private LinkedList<String> logs = new LinkedList<>();

    public void addLog(String logEntry) {
        logs.add(logEntry);
    }

    public void removeOldLogs(int daysAgo) {
        // 假设日志中有时间戳,可以根据时间戳删除超过指定天数的日志
        // 这里只是一个简单的示例,实际实现可能更复杂
        for (String log : logs) {
            // 根据条件判断是否删除日志
        }
    }

    public static void main(String[] args) {
        LoggingSystem loggingSystem = new LoggingSystem();
        loggingSystem.addLog("Log entry 1");
        loggingSystem.addLog("Log entry 2");
        // 后续可以根据需要添加和删除日志
    }
}

  2. 实时消息处理:

  • 在一个实时消息处理系统中,新的消息不断到来需要添加到消息队列中,同时已处理的消息可以从队列中删除。LinkedList 的高效插入和删除操作可以满足这种实时性要求。

  • 代码示例:

import java.util.LinkedList;

public class MessageProcessingSystem {
    private LinkedList<String> messages = new LinkedList<>();

    public void addMessage(String message) {
        messages.add(message);
    }

    public String processNextMessage() {
        return messages.removeFirst();
    }

    public static void main(String[] args) {
        MessageProcessingSystem messageSystem = new MessageProcessingSystem();
        messageSystem.addMessage("Message 1");
        messageSystem.addMessage("Message 2");
        System.out.println(messageSystem.processNextMessage()); // 输出:Message 1
        System.out.println(messageSystem.processNextMessage()); // 输出:Message 2
    }
}

 

  三:LinkedList源码分析:

  3.1 主要成员变量

• size:表示链表中元素的个数。

• first:指向链表的第一个节点。

• last:指向链表的最后一个节点。

  3.2 内部节点类

• item:存储节点中的元素。

• next:指向下一个节点的引用。

• prev:指向前一个节点的引用。

  3.3 构造方法

   默认构造方法:创建一个空的链表。

   包含集合参数的构造方法:可以从另一个集合创建LinkedList,将集合中的元素依次添加到链表中。

  3.4添加元素方法

1. add(E e):在链表末尾添加元素。

    内部调用linkLast方法,将新元素作为最后一个节点添加到链表中。

2. add(int index, E element):在指定位置插入元素。

    首先检查索引是否合法,然后根据索引位置决定是在末尾添加(调用linkLast)还是在指定位置插入(调用linkBefore)。

  3.5 删除元素方法

 1. remove(int index): 删除指定位置的元素。

首先检查索引是否合法,然后调用unlink方法删除指定节点。 


public E remove(int index) {
    checkElementIndex(index);
    return unlink(node(index));
}
   E unlink(Node<E> x) {
    // assert x!= null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}


2. remove(Object o):删除指定的元素。

遍历链表找到指定元素的节点,然后调用unlink方法删除该节点。


public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x!= null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x!= null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

  同理,其他的方法都可以通过源码阅读知道底层是怎么去实现的。

  四:模拟实现LinkedList

public class MyLinkedList {
    static class ListNode {
        public int val;
        public ListNode prev;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;//标记双向链表的头部
    public ListNode tail;//标记双向链表的尾部


    /**
     * 打印双向链表的每个节点 的值
     */
    public void display() {
        ListNode cur = this.head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    //得到链表的长度
    public int size() {
        int count = 0;
        ListNode cur = this.head;
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }


    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.tail = node;
        } else {
            node.next = this.head;
            this.head.prev = node;
            head = node;
        }
    }

    //尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.tail = node;
        } else {
            tail.next = node;
            node.prev = tail;
            tail = node;
        }
    }

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {
        //1、判断Index位置的合法性
        if (index < 0 || index > size()) {
            throw new IndexWrongFulException("index位置不合法!");
        }
        //2、判断特殊位置,头插 和 尾插
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        //3、找到index位置节点的地址
        ListNode cur = findIndexListNode(index);
        //4、修改4个指向
        ListNode node = new ListNode(data);
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
    }

    private ListNode findIndexListNode(int index) {
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }


    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                if (cur == head) {
                    head = head.next;
                    if (head != null) {
                        head.prev = null;
                    } else {
                        tail = null;
                    }
                } else {
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {
                        this.tail = cur.prev;
                    }
                }
                return;
            }
            cur = cur.next;
        }
    }

    //删除所有值为key的节点
    public void removeAllKey(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                if (cur == head) {
                    head = head.next;
                    if (head != null) {
                        head.prev = null;
                    } else {
                        tail = null;
                    }
                } else {
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {
                        this.tail = cur.prev;
                    }
                }
            }
            cur = cur.next;
        }
    }

    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.prev = null;
            cur.next = null;
            cur = curNext;
        }
        head = null;
        tail = null;
    }
}

  注意:上述代码中,只是简单模拟一下LinkedList中常用方法的实现逻辑,用的是基本数据类型,并没有像源码那样使用泛型,改成泛型更加通用,因为节点中的数据可能是引用数据类型或者自定义类型 。

  五:LinkedList细节

  5.1 插入和删除操作

  • LinkedList在进行插入和删除操作时非常高效,尤其是在链表中间插入或删除元素。但是,在进行大量的随机插入和删除操作时,需要注意维护链表的结构完整性,避免出现指针错误。

   在 Java 的LinkedList中进行频繁的插入和删除操作时,可以通过以下方法避免性能下降: 如果要在链表中间插入或删除元素,可以先使用迭代器找到目标位置,然后使用迭代器的add和remove方法进行操作。这样可以避免在遍历过程中频繁地调用get方法,提高性能。

LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    if (element.equals("banana")) {
        iterator.remove(); // 删除元素
    }
}
iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    if (element.equals("apple")) {
        iterator.add("orange"); // 在元素后插入
    }
}

   5.2 遍历方式选择

  • 可以使用迭代器、增强型 for 循环和普通 for 循环进行遍历。如果只需要顺序遍历链表,迭代器和增强型 for 循环是比较好的选择,简洁且高效。如果需要在遍历过程中同时进行插入或删除操作,迭代器是更安全的方式,因为它可以正确地处理链表结构的变化。

  5.3 LinkedList插入和删除元素的平均时间复杂度为 O(1),但在某些特殊情况下可能接近 O(n)。

  插入操作:

  在链表头部插入元素:只需要更新一个指针,将新节点的next指向原来的头节点,然后更新头节点为新节点。时间复杂度为 O(1)。

public void addFirst(E e) {
    linkFirst(e);
}

private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    if (f == null)
        last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

  在链表尾部插入元素:同样只需要更新一个指针,将新节点的prev指向原来的尾节点,然后更新尾节点为新节点。时间复杂度为 O(1)。

public void addLast(E e) {
    linkLast(e);
}

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

   在链表中间插入元素:需要先找到插入位置,然后调整前后节点的指针。如果已知插入位置的前驱节点,那么插入操作的时间复杂度为 O(1);如果需要从头开始遍历找到插入位置,那么时间复杂度接近 O(n),其中 n 是链表的长度。

   删除链表中间元素:需要先找到要删除的节点,然后调整前后节点的指针。如果已知要删除节点的前驱节点,那么删除操作的时间复杂度为 O(1);如果需要从头开始遍历找到要删除的节点,那么时间复杂度接近 O(n),其中 n 是链表的长度。

  5.4 类型安全问题

  在使用LinkedList时,需要注意类型安全问题。如果存储不同类型的元素,可能会引发ClassCastException异常。可以使用泛型来确保链表中存储的元素类型一致。 

​
LinkedList list = new LinkedList();
list.add(10);
list.add("hello");
int number = (int) list.get(1); // 这里会抛出 ClassCastException

​

  5.5集合类之间的转换

  可以将LinkedList转换为其他集合类,例如使用toArray()方法将其转换为数组,或者使用ArrayList的构造函数将其转换为ArrayList。但在进行转换时,需要注意数据类型的兼容性和性能开销。

  5.6 并发修改异常

  • 在使用迭代器遍历LinkedList的同时进行修改操作(如添加或删除元素),可能会导致ConcurrentModificationException异常。因为迭代器在遍历过程中会维护一个修改计数器,当检测到集合被外部修改时,就会抛出异常。为了避免这种情况,可以使用迭代器的remove()方法在遍历过程中进行安全的删除操作,或者在修改后重新获取迭代器。

LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    if (element.equals("banana")) {
        list.remove(element); // 这里会抛出 ConcurrentModificationException
    }
}

1. 如果在多线程环境下使用LinkedList,可以考虑使用并发安全的集合类,

如:ConcurrentLinkedQueue。这些集合类在设计时考虑了多线程并发访问的情况,提供了更高的性能和线程安全性。

2. 如果必须使用LinkedList,可以使用同步机制来保护对链表的访问。例如,可以使用synchronized关键字或者ReentrantLock来确保在多线程环境下对链表的操作是线程安全的。但需要注意同步带来的性能开销。

  5.7 空指针处理

  • 当从LinkedList中获取元素时,需要注意处理可能返回的null值,以避免空指针异常。特别是在使用迭代器遍历链表时,要确保对每个元素进行适当的空指针检查。

  5.8 内存管理问题

  1. 内存泄漏:如果在使用LinkedList的过程中,没有正确地管理对链表中元素的引用,可能会导致内存泄漏。例如,如果一个元素被插入到链表中,但在后续的操作中不再被需要,却仍然被链表中的节点引用着,那么这个元素所占用的内存就无法被垃圾回收器回收。方案:及时清理无用的引用:如果一个元素从链表中被删除,确保其引用被及时设置为null,以便垃圾回收器可以回收其占用的内存。

  2. 内存浪费:由于LinkedList的每个节点都需要额外的内存来存储前后节点的引用,对于存储大量小元素的情况,可能会导致相对较高的内存开销。相比之下,数组或其他更紧凑的数据结构可能会更节省内存。(方案:选择合适的数据结构)

  5.9特殊用途

  1. 双向链表的优势:LinkedList是双向链表,这意味着可以从链表的头部和尾部两个方向进行快速的插入和删除操作。例如,可以使用addFirst和removeFirst方法在链表头部进行高效的操作,这在一些特定的算法和数据结构中可能非常有用,如实现栈或队列的数据结构。

  2. 作为队列或栈的实现:虽然 Java 中有专门的Queue和Stack接口以及相应的实现类,但LinkedList可以很容易地用作队列或栈。例如,通过只使用addLast和removeFirst方法,可以将LinkedList用作队列;通过只使用addFirst和removeFirst方法,可以将其用作栈。

  3. 与其他数据结构的转换:LinkedList可以与其他数据结构进行转换,这在一些特定的场景下可能很有用。例如,可以将LinkedList转换为数组,或者将其转换为另一种集合类型。但在进行转换时,需要注意性能和类型安全问题。

  5.10 内存布局细节

  1. 节点结构:LinkedList的节点不仅包含数据元素,还包含前后节点的引用。这使得每个节点占用的内存相对较大,尤其是在存储小数据量时,与其他数据结构相比可能存在一定的内存浪费。例如,存储大量小整数时,ArrayList可能在内存使用上更加紧凑,因为它是基于数组实现的,而LinkedList的每个节点都需要额外的空间来存储指针。

  2. 内存碎片化:由于LinkedList的节点在内存中是分散存储的,频繁的插入和删除操作可能导致内存碎片化。虽然 Java 的垃圾回收机制会处理不再使用的节点,但在某些情况下,内存碎片化可能会影响程序的性能,尤其是在长时间运行的程序中。

  5.11 性能特性的微妙之处

  1. 遍历性能:虽然在插入和删除操作上表现出色,但LinkedList的遍历性能相对较差。这是因为在遍历过程中,需要逐个节点地通过指针进行访问,而不能像ArrayList那样通过索引快速定位元素。例如,在一个大型的LinkedList中进行顺序遍历可能比在同等大小的ArrayList中遍历要慢得多。

  2. 大尺寸链表的性能下降:当LinkedList变得非常大时,一些操作的性能可能会急剧下降。例如,在一个包含数十万甚至更多节点的链表中进行插入或删除操作,可能会因为需要遍历较长的距离来找到插入或删除位置而变得非常耗时。



 

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

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

相关文章

【教程】MySQL数据库学习笔记(六)——数据查询语言DQL(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 第三章 《数据定义语言DDL》 第四章 《数据操…

Day16_0.1基础学习MATLAB学习小技巧总结(16)——元胞数组

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

详细讲解hive on tez中各个参数作用,以及如何优化sql

最近经常有优化sql的任务&#xff0c;但是自己能力有限&#xff0c;只能凭经验去优化&#xff0c;现整理加学习一波&#xff0c;也欢迎各位学习和讨论。 我们经常用hivesql 的模型就是 join.如下。 insert overwrite table a select * from b left join c 这里面发生了什么…

【C++取经之路】map的详细介绍及其使用

目录 关于map 键值对 map的常用操作 关于multimap 关于map template < class Key, // map::key_typeclass T, // map::mapped_typeclass Compare less<Key>, //…

【时间盒子】-【5.绘制闹钟】动态绘制钟表和数字时间

Tips: Preview装饰器&#xff0c;支持组件可预览&#xff1b; Component装饰器&#xff0c;自定义组件&#xff1b; Canvas组件的使用&#xff1b; 使用RenderingContext在Canvas组件上绘制图形&#xff0c;请参考官方文档&#xff1a;https://developer.huawei.com/consume…

Apache ShardingSphere数据分片弹性伸缩加解密中间件

Apache ShardingSphere Apache ShardingSphere 是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。 软件背景 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding…

如何利用python实现碰撞原理

先看图 跑了大概一天 这是结果 具体是通过BIP39规则生成的种子数据 生成完词组后&#xff0c;再根据词组生成姨太地址 # 生成随机助记词 def generate_mnemonic():entropy os.urandom(16) # 随机生成 16 字节熵mnemonic []for i in range(12): # 生成 12 个助记词word_in…

欧拉数据库的搭建及其部署

数据库的搭建 进行数据库安装前&#xff0c;必须保证软件yum仓库搭建完成 使用命令 dnf install mariadb-server&#xff0c;发现冲突selinux-policy-targeted-35.5-21.oe2203sp3.noarch有问题 [rootlocalhost yum.repos.d]# dnf install mariadb-server [rootlocalhost y…

Arthas工具使用,分析线上问题好帮手

在K8S中的步骤&#xff1a; #1.进入node #2.下载arthas 在容器中下载并启动 Arthas&#xff1a; java -jar arthas-boot.jar --repo-mirror aliyun --use-http #3.找到出现问题的类和方法的绝对路径 类路径 方法 #4.执行trace命令或者watch命令 trace:命令会追踪方法的执…

电脑硬盘数据丢失了怎么恢复?简单实用的硬盘数据找回的方法

我们的电脑使用硬盘作为存储设备来保存数据&#xff0c;硬盘里的数据是存储在扇区上&#xff0c;这些存储数据的单元则位于表面有磁性材料的旋转的盘片上。硬盘内部的磁头悬浮于高速旋转的盘片上&#xff0c;用于读写和检索数据。 假如我们使用电脑时不小心删除了某个文件&…

iOS——weak修饰符的学习补充

Weak修饰符的内部机制 SideTable ObjectC中对对象的存储&#xff0c;实现上做了一定的优化&#xff0c;一旦有弱引用对象被赋值&#xff0c;即运行时&#xff08;Runtime&#xff09;会在全局的SideTables中分配一个SideTable空间&#xff0c;此空间是根据对象的地址相关算法…

多线程 | synchronized的底层原理

目录 1.它做了什么2.什么是Monitor如何减少用户态和内核态的切换 3.对象头和内置锁 (ObjectMonitor)3.1对象头3.2内置锁 (ObjectMonitor)3.3wait方法底层3.4notify 方法的底层实现 4.总结 1.它做了什么 使用synchronized的修饰的代码块如下&#xff0c;那么jvm是如何编译它的&…

【PyQt6 应用程序】短剧原视频直接生成解说视频精简版

在当今视频内容创作日益繁荣的时代,利用自动化工具进行视频编辑和二次创作已成为提高生产效率和创作水平的重要手段。本文将介绍如何使用PyQt6创建一个应用程序,该程序能够自动提取视频中的解说和原声部分,并使用人工智能生成配套的解说视频,从而生成具有独特风格的新视频内…

Oracle OCP认证值得考吗? 需要门槛吗?

随着数据量的爆炸性增长和企业对数据依赖性的提升&#xff0c;对数据库专业人士的需求也在不断上升。OCP认证&#xff0c;作为Oracle公司提供的权威认证之一&#xff0c;长期以来被视为数据库专业人士技能和知识水平的重要标志。 但随着技术的发展和认证种类的增多&#xff0c;…

基于百度AIStudio飞桨paddleRS-develop版道路模型开发训练

基于百度AIStudio飞桨paddleRS-develop版道路模型开发训练 参考地址&#xff1a;https://aistudio.baidu.com/projectdetail/8271882 基于python35paddle120env环境 预测可视化结果&#xff1a; &#xff08;一&#xff09;安装环境&#xff1a; 先上传本地下载的源代码Pad…

数据分析:R语言计算XGBoost线性回归模型的SHAP值

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍SHAP用途计算方法:应用加载R包导入数据数据预处理函数模型介绍 SHAP(SHapley Additive exPlanations)值是一种解释机器学习模型预测的方法。它基于博弈论中的Shapley值概念,…

Linux高性能服务器编程 总结索引 | 第3章:TCP协议详解

和IP协议相比&#xff0c;TCP协议 更靠近应用层&#xff0c;因此在应用程序中具有 更强的可操作性。一些重要的socket选项都和TCP协议相关 TCP头部信息。TCP头部信息出现在 每个TCP报文段中&#xff0c;用于指定 通信的源端口号、目的端口号&#xff0c;管理TCP连接&#xff0…

使用ffmpeg在视频中绘制矩形区域

由于项目需要对视频中的人脸做定位跟踪&#xff0c; 我先使用了人脸识别算法&#xff0c;对视频中的每个帧识别人脸、通过人脸库比对&#xff0c;最终记录坐标等信息。 然后使用ffmpeg中的 drawbox 滤镜功能&#xff0c;选择性的绘制区域。从而实现人脸定位跟踪 1、drawbox …

C++项目引入开源库bit7z

摘要&#xff1a; 公司C项目需要能解压缩.tar文件&#xff0c;关键是要在Windows环境下&#xff0c;tar格式主要是Linux中用的压缩文件&#xff0c;还要考虑到用户可能没有Windows自带的tar命令&#xff0c;最终解决方案就是一步到位&#xff0c;考虑到后续的功能拓展引入第三方…

尚品汇-延迟插件实现订单超时取消(四十五)

目录&#xff1a; &#xff08;1&#xff09;延迟插件封装 &#xff08;2&#xff09;基于延迟插件测试 如何保证消息幂等性&#xff1f; &#xff08;3&#xff09;改造订单service-order模块-实现订单超时取消 &#xff08;1&#xff09;延迟插件封装 把消息带过去&#…