《回爐重造 Java 基礎》——集合(容器)

来源:https://www.cnblogs.com/god23bin/archive/2022/05/28/relearn-collection-and-map.html
-Advertisement-
Play Games

整體框架 綠色代表介面/抽象類;藍色代表類。 主要由兩大介面組成,一個是「Collection」介面,另一個是「Map」介面。 前言 以前剛開始學習「集合」的時候,由於沒有好好預習,也沒有學好基礎知識,介面,類,這些基礎知識都沒學好,所以學到這裡還是懵懵懂懂的。第一次接觸到「集合」,這兩個字,在我的 ...


整體框架

集合(容器)

綠色代表介面/抽象類;藍色代表類。

主要由兩大介面組成,一個是「Collection」介面,另一個是「Map」介面。

前言

以前剛開始學習「集合」的時候,由於沒有好好預習,也沒有學好基礎知識,介面,類,這些基礎知識都沒學好,所以學到這裡還是懵懵懂懂的。第一次接觸到「集合」,這兩個字,在我的腦海中,只浮現出數學中學過的「集合」,所以當「集合」在編程語言中出現時,我就沒有繞過來。不過以我現在的視角看,也是和數學中學過的「集合」這種概念是差不多的。

數學中的「集合」:

集合是確定的一堆東西,集合里的東西則稱為元素。現代的集合一般被定義為:由一個或多個確定的元素所構成的整體。

Java 中的「集合」:在我的理解中,集合可以說是存放 Java 對象的東西,這個東西有人稱為集合,也有人稱為容器,這也是為什麼我的標題寫的是 集合(容器)。存放在集合中的對象,人們稱為元素。

為什麼會有集合的出現呢?

是這樣的,在某些情況下,我們需要創建許多 Java 對象,那麼這些對象應該存放在哪裡?

需求是這樣的:

  • 可以存放對象
  • 可以存放不同數據類型
  • 可以存放很多對象,沒有限制

那麼一開始會想到數組,數組可以存放對象,是的,沒錯,但是,數組有它的缺點,就是一旦創建後,那麼數組長度是不可變的,而且存放的對象的數據類型是固定的,所以數組不滿足這些條件。此時,集合就出現了

Java 中的集合

從上面的框架圖中可以看到,主要就兩個介面,分別是 CollectionMap

這兩個介面都抽象了元素的存儲方法,具體有什麼區別呢?好吧,不說也知道,Collection 就是用來存儲單一元素的,而 Map 是用來存儲鍵值對的。

下麵我將從這兩個介面切入,進而開始好好地回爐重造,哈哈哈哈哈。

可以帶著這些問題去回顧:

  • Collection 是怎樣的?Map 又是怎樣的?
  • 它們分別還有什麼子介面?它們又有哪些實現類呢?
  • 提供給我們的API又有哪些呢?具體的 API 用法和效果是怎樣的呢?

Collection

Collection 是最基本的集合介面,一個 Collection 代表一組 Object類型的對象,Java 沒有提供直接實現Collection 的類,只提供繼承該介面的子介面(List、Set、Queue 這些)。該介面存儲一組不唯一,無序的對象。這裡強調不唯一、無序,那麼集合的範圍就很大,想要縮小,比如唯一、有序這些,就可以通過子介面來規定,剛好,它就是這樣來定義子介面的。

  • List 介面:元素不唯一且有序,說明可以存儲多個相同的元素,但是存儲是有順序的,即有序可重覆
  • Set 介面:元素唯一且無序,說明不能存儲多個相同的元素,存儲的元素沒有順序,即無序不可重覆

我們再來看看 Collection 介面它抽象出來的方法有哪些。

Collection 介面它抽象出來的方法

其中,還可以看到有個以 Iterable(可迭代的) 來分類的方法,主要就是 iterator() 這個方法,即迭代器。所謂迭代器,就是用來遍歷集合元素的一個東西。

    /**
     * Returns an iterator over the elements in this collection.  There are no
     * guarantees concerning the order in which the elements are returned
     * (unless this collection is an instance of some class that provides a
     * guarantee).
     *
     * @return an <tt>Iterator</tt> over the elements in this collection
     */
    Iterator<E> iterator();

iterator() 這個方法就是用來返回對此集合中元素的迭代器,也就是說獲取該集合的迭代器。 這個抽象方法不保證迭代的順序(除非此集合是某個提供保證的類的實例)。

再通俗一點,我們想要遍歷集合的元素,那麼就需要通過集合對象獲取迭代器對象,通過迭代器對象來遍歷集合中的元素,而且遍歷的順序是跟該集合有關的。

迭代器介面抽象出來的方法

關於這個迭代器,後續再來講吧。

下麵開始說一下基本的介面實現類,基本的API,加上一些自己的見解,最主要是先回顧 API 的使用,畢竟還有好多知識,這些知識需要建立在我們會用的前提下,所以這裡淺入淺出~

List 介面下的實現類

List 介面下的實現類有 ArrayListLinkedListVectorStack

List介面的實現類

這裡簡要介紹下 List 介面,List 介面是一個有序的 Collection,使用此介面能夠精確的控制每個元素插入的位置,能夠通過「索引」(即元素在 List 中位置,類似於數組的下標,從0開始)來訪問 List 中的元素。它存儲一組不唯一、有序(插入順序)的對象。

ArrayList

我們看下 ArrayList 源碼,它是這樣定義的:

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

可以看到,它

  • 繼承了 AbstractList
  • 實現了 ListRandomAccessCloneableSerializable

ArrayList 是動態數組,所謂動態數組,即可以動態的修改,隨時出入、刪除元素,還可以動態擴容,也就是沒有固定的容量限制,可以存放很多元素,直到你的記憶體爆炸。

初始化是這樣的:

// 1. 以多態的方式寫,介面不能實例化,所以通過其實現類對介面實例化。
List<E> list = new ArrayList<>();
// 2. 直接ArrayList
ArrayList<E> list = new ArrayList<>();

這兩種寫法有什麼區別呢?

第1種寫法:此時的 List 的對象 list,可以通過這個對象調用 List 介面聲明的方法,但是不能調用 ArrayList 獨有的方法,換句話說,List 這個介面規定了一些抽象的方法,具體實現不關心,你可以直接調用。這裡「具體實現不關心」就是說,你是使用 ArrayList 來實例化 List 介面或者使用 LinkedList 來實例化 List 介面,List 介面它都不關心,外界使用的時候,知道 List 提供這些 API 就夠了。另一個角度理解,即該 list 對象擁有 List 的屬性和方法,沒有 ArrayList 獨有的屬性和方法。

第2種寫法:此時 ArrayList 的對象 list,可以調用所有方法,畢竟 ArrayList 實現了 List 介面,那麼 List 有的方法,ArrayList 的對象 list也有。

進入正題

ArrayList需要掌握的方法

這些 API 的使用,需要熟悉,畢竟演算法題也會用到。

    public void apiOfArrayList() {
        int idx;
        List<Integer> list = new ArrayList<>();

        // 添加元素
        list.add(23);
        list.add(30);

        // 根據下標(索引)獲取元素
        idx = list.get(0);
        idx = list.get(1);

        // 更新元素值,在某個位置重新賦值
        list.set(1, 32);

        List<String> list2 = new ArrayList<>();
        list2.add("god23bin");
        list2.add("LeBron");
        list2.add("I love Coding");

        // 移除下標(索引)為2的元素
        list2.remove(2);

        // 移除指定元素
        list2.remove("god23bin");

        // 獲取集合長度,遍歷會用到
        int len = list2.size();

        // 判斷某個元素是否在集合中,演算法題會用到的
        boolean flag = list2.contains("god23bin");

        // 判斷集合是否為空,演算法題會用到的
        boolean flag2 = list2.isEmpty();

    }

排序:

  • ArrayList 中的 sort() 方法
Random random = new Random();
List<Integer> numList = new ArrayList<>();
for (int i = 0; i < 10; ++i) {
    numList.add(random.nextInt(100));
}
// 將numList升序排序
numList.sort(Comparator.naturalOrder());
// 將numList降序排序
numList.sort(Comparator.reverseOrder());
  • Collections 工具類的 sort() 方法
// 將numList升序排序
Collections.sort(numList);

關於 Collections 工具類的排序

看看這兩個方法,這兩個方法都是泛型方法。

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

public static <T> void sort(List<T> list, Comparator<? super T> c) {
    list.sort(c);
}

第一個方法需要「待排序類」實現 Comparable 介面,這樣才能使用這個方法。

第二個方法需要「待排序類」有一個比較器,即 Comparator,換句話說需要有一個比較器類實現 Comparator 介面,這樣才能使用這個方法。

扯到 Comparable 和 Comparator

所以,如果你想要某個類的對象支持排序,那麼你就需要讓這個類實現 Comparable 介面,這個介面只有一個抽象方法 compareTo(),我們需要實現它,它的規則是:若 當前值 較大則返回正值,若相等則返回0,若 當前值 較小則返回負值。

這裡我們可以看到,Collections 是可以對 numList 進行排序的,因為這個 numList 集合的元素類型是 Integer,為什麼 Integer 類型的元素支持排序?我們可以從源碼中看到 Integer 是實現了 Comparable 介面的,所以 Integer 類型的元素才支持排序。

public final class Integer extends Number implements Comparable<Integer> {
    ...
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
    
    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
    ...
}

回到第一句話,如果你想要某個類的對象支持排序,那麼你就需要讓這個類實現 Comparable 介面,不然是不支持排序的。

下麵,我這裡就分別使用兩種方式(實現 Comparable 或 Comparator)讓某個類支持排序。

搞定 Comparable

舉個慄子:我這裡有一個 Game 類(待排序類,本身不支持排序,我們的任務是讓 Game 具有可排序的能力),當你把多個 Game 對象放到集合中使用 Collections 這個工具類進行排序時,Collections 是不知道如何給 Game 排序的,直到 Game 實現了 Comparable 介面後,Collections 才知道 Game 該如何排序。

Game 類實現 Comparable 介面,重寫 compareTo() 方法。

public class Game implements Comparable<Game> {

    public String name;
    public Double price;

    // 省略 getter setter 構造方法

    @Override
    public int compareTo(Game o) {
        return comparePrice(this.price, o.price);
    }

    public int comparePrice(double p1, double p2) {
        return p1 > p2 ? 1 : (p1 == p2 ? 0 : -1);
    }
}

這樣,我們就可以使用 Collections 對 Game 進行排序。

List<Game> gameList = new ArrayList<>();
gameList.add(new Game("GTA", 58.0));
gameList.add(new Game("FC", 118.0));
gameList.add(new Game("2K", 199.0));
Collections.sort(gameList);		// 進行排序
System.out.println(gameList);	// 列印排序結果

搞定 Comparator

同理,我這裡有一個 Game 類

public class Game {

    public String name;
    public Double price;

    // 省略 getter setter 構造方法
}

寫一個 Game 的比較器類 GameComparator,讓這個類實現 Comparator 介面,重寫 compare() 方法

public class GameComparator implements Comparator<Game> {
    @Override
    public int compare(Game g1, Game g2) {
        return g1.getPrice() - g2.getPrice();
    }
}

這樣,我們就可以使用 Collections 對 Game 進行排序。

List<Game> gameList = new ArrayList<>();
gameList.add(new Game("GTA", 58.0));
gameList.add(new Game("FC", 118.0));
gameList.add(new Game("2K", 199.0));
Collections.sort(gameList, new GameComparator());		// 使用比較器進行排序
System.out.println(gameList);							// 列印排序結果

總結排序

你可以選擇兩種方式(實現 Comparable 或 Comparator)中的其中一個,讓某個類支持排序。

  • 選擇 Comparable,那麼該類需要實現該介面
  • 選擇 Comparator,那麼需要定義一個比較器,實現該介面

最後通過 Collections.sort() 進行排序。

LinkedList

LinkedList 是鏈表,屬於線性表,學過數據結構的我們也是知道的,有指針域和數據域,雖然說 Java 里沒有指針,但是有指針的思想,這裡我也說不太清楚,反正是可以按指針來理解的。(如有更好的描述,歡迎幫我補充啦!)

在 Java 中,這個 LinkedList 是 List 介面下麵的實現類,也是很常用的一種集合容器,演算法題也會用到它。

我們看下 LinkedList 源碼,它是這樣定義的:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    ...
}

可以看到,它

  • 繼承了 AbstractSequentialList
  • 實現了 ListDequeCloneableSerializable

同樣,LinkedList 需要掌握的方法和 ArrayList 差不多,可以說基本是一樣的,只是底層實現不一樣。

LinkedList需要掌握的方法

目前這裡就不演示基本的使用方法了,你可以自己動手試試啦!

Vector

我們看下 Vector 源碼,它是這樣定義的:

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

可以看到,它

  • 繼承了 AbstractList
  • 實現了 ListRandomAccessCloneableSerializable

這樣一看,它和 ArrayList 的定義,簡直是一模一樣。那它們之間有什麼區別嗎?那當然是有啦!

區別就是 Vector 是線程安全的,在多線程操作下不會出現併發問題,因為 Vector 在每個方法上都加上了 synchronized 關鍵字,保證多個線程操作方法時是同步的。

Stack

Stack 顧名思義,就是棧,它是 Vector 的子類,實現了標準的棧這種數據結構。

public class Stack<E> extends Vector<E> {
    ...
}

它裡面包括了 Vector 的方法,也有自己的方法。

  • empty():判斷棧是否為空
  • peek():查看棧頂元素
  • push():入棧
  • pop():出棧
  • search():搜索元素,返回元素所在位置
    public void apiOfStack() {
        Stack<Integer> stack = new Stack<>();
        // 入棧
        stack.push(1);
        stack.push(2);
        stack.push(3);
        stack.push(4);
        // 獲取棧的大小 == 棧中元素個數 == 棧的長度
        int size = stack.size();
        // 查看(返回)棧頂元素
        Integer peek = stack.peek();
        // 出棧
        stack.pop();
        // 判斷棧是否為空
        boolean empty = stack.empty();
        // 搜索 元素1 此時棧中元素為 1 2 3,棧頂是3,棧底是1
        // 從棧頂往下找,第一個元素的位置記為1
        int search = stack.search(1);
    }

但是目前這個已經官方不推薦使用了,而是選擇使用 LinkedList 來用作棧

這裡就要扯到隊列 Queue 啦!

Queue 介面

Java 中的 Queue 是一個介面,和上面的 Stack 不同,Stack 是類。

我們看下 Queue 介面源碼,它是這樣定義的:

public interface Queue<E> extends Collection<E> {
    ...
}

這個介面就抽象了 6 個方法:

  • add():入隊,即隊尾插入元素
  • offer():入隊,即隊尾插入元素
  • peek():查看隊頭元素
  • poll():查看隊頭元素
  • remove():出隊,即移除隊頭元素
  • element():出隊,即移除隊頭元素

很大的疑問來了!這些方法有什麼區別??我們看看源碼怎麼說的,這個源碼說明也不怕,下麵我有翻譯~

add() 和 offer() 的區別

    /**
     * Inserts the specified element into this queue if it is possible to do so
     * immediately without violating capacity restrictions, returning
     * {@code true} upon success and throwing an {@code IllegalStateException}
     * if no space is currently available.
     *
     * @param e the element to add
     * @return {@code true} (as specified by {@link Collection#add})
     * @throws IllegalStateException if the element cannot be added at this
     *         time due to capacity restrictions
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this queue
     * @throws NullPointerException if the specified element is null and
     *         this queue does not permit null elements
     * @throws IllegalArgumentException if some property of this element
     *         prevents it from being added to this queue
     */

    /**
     * 將指定的元素插入此隊列(如果可以立即執行此操作而不違反容量限制),成功則返回 true
     * 如果容量不夠,則失敗,拋出 IllegalStateException
     */
    boolean add(E e);

    /**
     * Inserts the specified element into this queue if it is possible to do
     * so immediately without violating capacity restrictions.
     * When using a capacity-restricted queue, this method is generally
     * preferable to {@link #add}, which can fail to insert an element only
     * by throwing an exception.
     *
     * @param e the element to add
     * @return {@code true} if the element was added to this queue, else
     *         {@code false}
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this queue
     * @throws NullPointerException if the specified element is null and
     *         this queue does not permit null elements
     * @throws IllegalArgumentException if some property of this element
     *         prevents it from being added to this queue
     */
    /**
     * 如果可以在不違反容量限制的情況下立即將指定的元素插入到此隊列中。
     * 使用容量受限的隊列時,通常此方法是最好的入隊方法 ,只有當引發異常時才可能無法插入元素。
     * 成功返回 true,失敗返回 false
     */
    boolean offer(E e);

所以區別就是:在容量有限制的隊列中,add() 超過限制會拋出異常,而 offer() 不會,只會返回 false

remove() 和 poll() 的區別

    /**
     * Retrieves and removes the head of this queue.  This method differs
     * from {@link #poll poll} only in that it throws an exception if this
     * queue is empty.
     *
     * @return the head of this queue
     * @throws NoSuchElementException if this queue is empty
     */
	/**
     * 檢索並刪除此隊列的隊頭元素。  這個方法與 poll 的區別僅僅是當隊列為空時刪除會拋出異常。
     */
    E remove();

    /**
     * Retrieves and removes the head of this queue,
     * or returns {@code null} if this queue is empty.
     *
     * @return the head of this queue, or {@code null} if this queue is empty
     */
	/**
     * 檢索並刪除此隊列的隊頭元素。如果隊空,則返回 null
     * or returns {@code null} if this queue is empty.
     *
     * @return the head of this queue, or {@code null} if this queue is empty
     */
    E poll();

所以區別是:當隊空時刪除元素,那麼 remove() 會拋出異常, poll() 會返回null

element() 和 peek() 的區別

    /**
     * Retrieves, but does not remove, the head of this queue.  This method
     * differs from {@link #peek peek} only in that it throws an exception
     * if this queue is empty.
     *
     * @return the head of this queue
     * @throws NoSuchElementException if this queue is empty
     */
    /**
     * 隊列為空時會拋出異常
     */
    E element();

	/**
     * Retrieves, but does not remove, the head of this queue,
     * or returns {@code null} if this queue is empty.
     *
     * @return the head of this queue, or {@code null} if this queue is empty
     */
	/**
	 * 隊列為空時會返回 null
     */
    E peek();

所以區別是:當隊空查看隊頭元素時,那麼 element() 會拋出異常, peek() 會返回null

總的來說,就是失敗的區別:

拋出異常 返回特殊值
入隊 add() offer() 返回false
出隊 remove() poll() 返回 null
查看隊頭元素 element() peek() 返回 null

雙端隊列和優先順序隊列

Queue介面

Queue 有個子介面 Deque,就是雙端隊列,需要掌握的 Deque 實現類為 LinkedListArrayDeque。然後我們可以發現 Deque 這個介面抽象出來的方法,在原有的 Queue 上,多出了 First、Last 這些方法,對應著就是從隊列的頭部和尾部進行操作(入隊、出隊等等)。

有個抽象類 AbstractQueue 實現了 Queue 介面,然後 PriorityQueue 繼承了 AbstractQueue

演示下基本的 API,大部分操作都是大同小異。

    public void apiOfDeque() {
        // 通過 LinkedList 創建 Deque 對象
        Deque<Integer> deque = new LinkedList<>();
        // 正常隊尾入隊 => 完成入隊後 [1,2,3]
        deque.addLast(1);
        deque.addLast(2);
        deque.addLast(3);
        // 從隊頭入隊 => [4,1,2,3]
        deque.addFirst(4);
        // 獲取隊頭元素 => 4
        // 這裡 get 和 peek 的區別就是,get 如果隊空會拋出異常
        Integer first = deque.getFirst();
        // 使用 offer 入隊 => [4,1,2,3,5]
        deque.offerLast(5);
        // 使用 poll 出隊 => [1,2,3,5]
        Integer integer = deque.pollFirst();
        // 剩下的操作也是差不多的...
    }

至於優先順序隊列的呢?之後再寫啦!這個坑等著後面補回來

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原文地址: 關於TornadoFx和Android的全局配置工具類封裝實現及思路解析 - Stars-One的雜貨小窩 目前個人開發軟體存在設置頁面,可以讓用戶自定義些設置,但我發現,存儲數據的代碼邏輯實在是有些繁瑣(保存及APP打開的設置初始化) 於是便是花了些精力研究了些,封裝了個簡單的工具類, ...
  • Vue框架搭建項目時所用的vue官方項目模版,通過腳手架,下載vue項目,如何創建項目,認識項目結構和拉取項目模版。 ...
  • DOM獲取元素、修改元素 1.DOM ①什麼是DOM?作用? DOM是文檔對象模型作用:操作網頁內容,可以開髮網頁內容特效和實現用戶交互。 ②DOM對象 2.獲取DOM元素 ① 根據CSS選擇器來獲取DOM元素 (重點) 思考 獲取一個DOM元素我們使用誰?querySelector() 獲取多個D ...
  • 1 /* ColorTestViewer 顏色調試器 2 3 attribute: 4 onchange: Function; //顏色改變回調; 預設null 5 6 //以下屬性不建議直接修改 7 rgb: RGBColor; //rgb模式顏色 8 hsv: Object{h,s,v}; // ...
  • 1.字元集/字元編碼是什麼? 字元集或者說字元編碼就是給字元定義了數值編號以及數值編號存儲格式。 嚴格來說字元集和字元編碼是兩個概念: charset 是 character set 的簡寫,即字元集。 encoding 是 charset encoding 的簡寫,即字元集編碼,簡稱編碼。 字元集 ...
  • HashMap源碼 目錄 1.1 包含的屬性 1.2 構造器 1.3 hash方法源碼 1.4 put源碼 1.5 resize源碼 1.6 table 變數為什麼用transient 修飾 1.1 包含的屬性 public class HashMap<K,V> extends AbstractMa ...
  • 前言 最近刷b站,看到一位up主利用python製作了一個校園網自動連接的程式,看完很是心動。想著自己也學過python,準備嘗試一下。前前後後摸索了兩天,這裡分享一下實現過程。 一、前期準備 導入需要的庫 import requests #請求鏈接 import base64 #密碼加密 impo ...
  • 對Java_lambda表達式 Stream流 Option類進行總結和簡要介紹 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...