Java-HashMap基礎與擴展學習總結

?面試官?:

“HashMap 是 Java 中最常用的數據結構之一,你能說說它的底層實現嗎?比如哈希沖突是怎么解決的?”

??(結合源碼與優化場景):
“好的,HashMap 底層是數組+鏈表/紅黑樹的結構。比如我們項目里的用戶標簽系統,用 HashMap 存儲用戶ID和標簽數據,當兩個用戶的哈希值沖突時,會以鏈表形式掛在同一個數組位置。

但鏈表過長會影響查詢效率,所以在 JDK8 之后,當鏈表長度超過8且數組長度≥64時,鏈表會轉成紅黑樹,這樣即使有10萬條數據,查詢時間也能從O(n)降到O(log n)。我們之前做性能測試時,插入10萬條數據,紅黑樹比鏈表快了近10倍。”


?面試官追問?:

“你提到紅黑樹轉換,為什么是鏈表長度≥8且數組長度≥64才轉換?為什么不直接用紅黑樹?”

??(分析設計權衡):
“這是空間和時間的權衡。紅黑樹雖然查詢快,但每個節點需要額外存儲父節點、顏色標記等字段,內存是鏈表的2倍。如果數組長度小(比如16),哈希沖突可能只是暫時的,擴容后沖突會減少,此時用鏈表更省內存。

比如我們有個配置中心的功能,初始容量設得小,大部分情況鏈表長度不會超過4,用紅黑樹反而浪費內存。所以HashMap的設計者通過統計發現,哈希沖突導致鏈表長度≥8的概率極低(約千萬分之一),這時才值得用紅黑樹換時間。”


?面試官深入?:

“HashMap 的擴容機制是怎樣的?為什么每次擴容是2倍?”

??(結合位運算優化):
“擴容時,數組會翻倍到原來的2倍(比如16→32),這樣計算新索引可以直接用高位掩碼。比如舊容量是16(二進制10000),擴容后是32(100000),元素的新索引要么在原位置,要么在原位置+舊容量

這樣做的好處是避免重新計算哈希,只需判斷高位是否為1。比如我們有個數據遷移工具,HashMap擴容時遷移數據的時間減少了30%,因為位運算比重新哈希快得多。”


?面試官挑戰?:

“在多線程環境下,HashMap 可能導致死循環,你能解釋下原因嗎?”

??(結合JDK7源碼缺陷):
“在JDK7中,HashMap擴容時采用頭插法轉移鏈表。假設線程A和線程B同時擴容,A剛將節點1→2→3反轉為3→2→1,此時B開始操作,可能把1→3→2,形成環形鏈表。后續查詢時遍歷鏈表會進入死循環。

我們線上就遇到過這個問題!當時用HashMap緩存實時日志,高并發下CPU飆到100%,用jstack發現線程卡在get()方法里。后來換成ConcurrentHashMap,問題解決。”


?面試官追問?:

“ConcurrentHashMap 在JDK7和JDK8的實現有什么區別?為什么JDK8要改成CAS+synchronized?”

??(對比設計演進):
“JDK7的ConcurrentHashMap用分段鎖(Segment),默認16個段,相當于16把鎖。比如我們有個風控系統,用ConcurrentHashMap計數,16個段的吞吐量上限明顯,無法充分利用多核CPU。

JDK8改成了CAS+synchronized鎖單個桶頭節點。比如插入數據時,先用CAS嘗試無鎖更新,失敗后再鎖住頭節點。這樣鎖粒度更細,并發度更高。我們測試過,同樣的16線程寫操作,JDK8的吞吐量是JDK7的2倍。”


?面試官:??
“你提到HashMap的擴容是翻倍,比如16→32,那新位置的計算是怎么優化的?為什么用2的冪次方作為容量?”

?你(結合位運算+源碼):??
“HashMap的容量必須是2的冪次方,核心原因是為了將取模運算轉化為位運算,提升性能。比如哈希值h,數組長度n=16,計算索引時用 h & (n-1) 代替 h % n

舉個具體例子:假設h=25,n=16,25%16=9,而二進制25是11001,n-1=15是01111,按位與結果也是01001(即9)。這種位運算比取模快得多,尤其在大量數據插入時。

擴容時,新容量是舊容量的2倍,這樣新索引只有兩種可能:原位置或原位置+舊容量。比如舊容量16時,h=25的索引是9;擴容到32后,n-1=31(11111),此時計算h=25 & 31=25,而25在原索引9的基礎上,實際上是9+16=25。這種設計避免了重新計算哈希,只需判斷哈希值的最高位是0還是1。”

?面試官(追問哈希擾動函數):??
“你提到哈希值的計算,HashMap的hash()方法里為什么要做異或位移?”

?你(結合哈希碰撞優化):??
“HashMap的hash()方法并不是直接使用Object的hashCode,而是對hashCode做了一次擾動:

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

比如一個hashCode是 0x12345678,右移16位得到0x00001234,異或后變成0x1234444C。這樣做是為了讓高位參與運算,減少哈希沖突。

舉個實際例子:假設數組長度是16,大部分鍵的hashCode高位不同但低位相同(比如0x0000 0001和0xFFFF 0001),如果不做擾動,這兩個鍵會被映射到同一個桶。但擾動后,高位信息被混合到低位,分布更均勻。”


?面試官(轉向ConcurrentHashMap):??
“ConcurrentHashMap在JDK8中為什么要放棄分段鎖?CAS具體用在哪些地方?”

?你(對比設計演進+源碼細節):??
“JDK7的ConcurrentHashMap用Segment分段鎖,默認16個Segment,相當于16個獨立的HashMap。這種設計的問題是,并發度被Segment數量限制,比如16個Segment最多支持16個線程并發寫。

JDK8改成了對每個桶的頭節點加鎖(synchronized),同時用CAS實現無鎖化初始化。比如插入元素時:

  1. ?初始化桶數組?:用CAS設置數組的引用,避免多個線程重復初始化。
  2. ?插入空桶?:如果桶是空的,用CAS將新節點設置為頭節點(避免鎖)。
  3. ?更新size?:用CounterCell數組分散線程競爭,避免單一AtomicLong的瓶頸。

我們做過壓測,16線程并發寫入時,JDK8的ConcurrentHashMap吞吐量是JDK7的3倍,因為鎖粒度從段級別細化到桶級別。”


?面試官(追問CAS的ABA問題):??
“ConcurrentHashMap的CAS操作會遇到ABA問題嗎?怎么解決的?”

?你(結合內存模型+實戰場景):??
“ABA問題通常發生在‘先讀后寫’的場景,比如一個線程讀到值是A,準備改成C,但中間另一個線程把A→B→A。但ConcurrentHashMap的CAS操作大多是針對引用(比如桶頭節點),而引用地址不會重復復用。

舉個具體例子:線程1準備將頭節點從A改為C,此時線程2已經移除了A并重新插入了一個新節點(地址不同)。即使鍵值相同,新節點的對象地址也不同,CAS會失敗,線程1需要重試。所以實際上,ABA問題在這里不會造成數據錯亂。”


?面試官(擴展知識點):??
“除了ConcurrentHashMap,還有其他線程安全的Map結構嗎?比如CopyOnWriteArrayMap?”

?你(對比+選型建議):??
“Android中的CopyOnWriteArrayMap通過復制整個數組實現線程安全,適合讀多寫極少的場景(比如全局配置)。但它每次寫操作都要復制數組,性能差,大數據量下慎用。

如果是高并發寫場景,ConcurrentHashMap仍然是首選。但要注意,ConcurrentHashMap的迭代器是弱一致性的(遍歷時可能讀到其他線程的修改),而Hashtable的迭代器是強一致性的,但性能差。

比如我們有個實時日志系統,用ConcurrentHashMap緩存日志條目,每秒上萬次put操作,配合異步線程批量導出數據,這時候弱一致性迭代器反而能避免阻塞主線程。”


?面試官(綜合實戰題):??
“現在有一個需求:統計一篇文章中每個單詞的出現次數,用多線程處理,你會怎么設計?”

?你(結合ConcurrentHashMap特性):??
“我會將文章拆分成多個段落,每個線程處理一段。所有線程共享一個ConcurrentHashMap,鍵是單詞,值是AtomicInteger。

核心代碼如下:

ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap<>();// 每個線程處理一段文本
void processText(String text) {String[] words = text.split(" ");for (String word : words) {// 如果單詞不存在,原子性地初始化一個AtomicIntegermap.computeIfAbsent(word, k -> new AtomicInteger()).incrementAndGet();}
}

這樣做的好處:

  1. ?computeIfAbsent()是原子方法?:避免多個線程重復創建AtomicInteger。
  2. ?AtomicInteger自增無鎖競爭?:每個單詞的計數器獨立,線程間不會互相阻塞。

我們做過類似的關鍵詞統計功能,8線程處理10萬單詞,ConcurrentHashMap的耗時比Hashtable減少了85%。”


?面試官(陷阱題):??
“ConcurrentHashMap的size()方法返回的值一定準確嗎?為什么?”

?你(深入CounterCell設計):??
“不一定準確!ConcurrentHashMap的size()實現用了分片計數。每個線程修改size時,先嘗試用CAS更新baseCount,失敗后會將計數分配到CounterCell數組。最終size()的結果是baseCount加上所有CounterCell的值。

但高并發下,可能有線程正在更新CounterCell,導致size()讀到的中間結果不準確。比如我們有個監控系統,用ConcurrentHashMap緩存設備狀態,size()顯示1024,但實際可能是1023或1025。如果需要精確統計,可以用mappingCount()方法返回long類型,或者改用AtomicLong累加。”


面試官(開場切入):??
“我看你簡歷里提到熟悉Android中的數據結構優化,能說說HashMap在Java中的實現原理嗎?比如它怎么解決哈希沖突的?”

?你(自然引出項目經驗):??
“好的,HashMap底層其實是個數組,每個數組位置叫桶(bucket)。比如我們項目里用HashMap緩存用戶信息,用戶ID作為鍵。當兩個不同ID算出的哈希值相同時,就會在同一個桶里用鏈表串起來。

但鏈表太長的話,比如用戶量暴漲到幾十萬,查詢會變慢。后來發現JDK8做了優化——當鏈表長度超過8,并且整個數組長度達到64時,鏈表會自動轉成紅黑樹。您可能猜到了,紅黑樹的查找是O(log n),比鏈表的O(n)快得多。我們壓測時插入了10萬條數據,紅黑樹版本的查詢速度比鏈表快了近10倍,這個優化對高并發場景特別關鍵。”


?面試官(追問設計細節):??
“有意思,但為什么非要等到鏈表長度到8才轉紅黑樹?為什么不一開始就用紅黑樹?”

?你(結合數據談權衡):??
“這其實是空間換時間的經典取舍。紅黑樹每個節點要存父節點、左右子節點,還有顏色標記,內存占用是鏈表的2倍。如果數組本身很小,比如初始容量16,這時候擴容可能比轉樹更劃算。

舉個實際例子:我們團隊有個配置中心的模塊,初期數據量小,大部分桶里鏈表長度不超過3。如果這時候強行用紅黑樹,內存會多消耗一倍,反而得不償失。后來看源碼注釋才知道,HashMap設計者統計過哈希碰撞的概率,鏈表長度到8的概率不到千萬分之一。換句話說,這個閾值是在極端情況下才觸發的安全網。”


?面試官(場景化問題):??
“假設現在有個需求:實現一個購物車的商品數量統計,鍵是商品ID(長整型),值是該商品的數量。用HashMap還是SparseArray更合適?為什么?”

?你(對比+實戰舉例):??
“如果是在Android環境下,我會優先考慮SparseArray。因為商品ID通常是整數(比如從服務端返回的id=10001),用SparseArray能避免自動裝箱產生的Integer對象,內存更緊湊。之前我們做性能優化時,用Android Profiler對比過,同樣的1萬條數據,SparseArray比HashMap省了約40%的內存。

不過要注意,SparseArray的查找是二分法,時間復雜度O(log n),適合數據量小的場景。比如購物車一般商品數量在幾百個以內,這時候O(log n)和HashMap的O(1)差異不大。但如果商品數可能破萬,或者需要頻繁刪除/插入,就得回到HashMap或者ArrayMap的懷抱了。”


?面試官(陷阱題):??
“你剛才提到SparseArray用二分查找,那它的鍵數組必須是有序的嗎?如果我插入的鍵是亂序的會發生什么?”

?你(暴露原理+避坑):??
“沒錯,SparseArray的鍵數組必須保持有序!比如我們團隊有個新人曾經在插入時沒注意順序,結果發現用get()方法經常返回null。后來排查發現,亂序插入會導致二分查找錯位。

舉個例子:假設已經存了鍵為10和20,這時候插入一個鍵為15的,SparseArray會把它放在10和20之間,維持數組有序。但如果直接調用put(5, value),它會通過二分查找找到該插入的位置,然后把后面的元素整體右移——這個過程有點像ArrayList的插入,所以頻繁插入中間位置的話,性能會比HashMap差很多。”


?面試官(深入原理):??
“那你說說,SparseArray的刪除操作是怎么實現的?直接縮容數組嗎?”

?你(結合源碼邏輯):??
“其實SparseArray用了延遲刪除的策略。比如刪除一個鍵時,它并不會立刻縮容數組,而是把對應位置的值標記為DELETE(一個靜態Object對象)。這樣做的好處是,下次插入新數據時可以直接復用這個‘空洞’,避免頻繁擴容縮容。

不過這也帶來了隱患——如果長期不插入新數據,這些DELETE標記會一直占用內存。所以SparseArray提供了gc()方法,它會遍歷數組,清理所有DELETE標記并重新排列元素。我們項目里的做法是,在數據批量刪除后(比如清空購物車),手動調用一次gc(),避免內存泄漏。”


?面試官(開放問題):??
“如果讓你設計一個圖片緩存,支持LRU淘汰策略,你會用LinkedHashMap還是自己實現?”

?你(方案對比+決策):??
“如果是快速實現原型,可以直接用LinkedHashMap的accessOrder模式,重寫removeEldestEntry()方法。比如這樣寫(假裝手寫代碼):

new LinkedHashMap<K, V>(initialSize, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > MAX_CACHE_SIZE;}
};

但實際項目中,我們遇到過LinkedHashMap在并發場景下的線程安全問題。后來改用AndroidX的LruCache,它內部用LinkedHashMap加同步鎖,更穩妥。如果要追求更高并發,比如像Glide那樣的圖片加載庫,就得考慮用讀寫鎖或者分段鎖了,這時候可能需要自己封裝結構。”


?面試官(收尾驗證):??
“最后一個問題:ArrayList和LinkedList在中間插入元素時,時間復雜度都是O(n),那它們的實際性能有差異嗎?為什么?”

?你(微觀分析+實戰數據):??
“雖然理論復雜度相同,但實際差異很大!比如在中間插入一個元素,ArrayList需要把后面的元素逐個往后拷貝,而LinkedList需要遍歷到那個位置再修改指針。

我們做過一個極端測試:在長度為10萬的ArrayList和LinkedList的中間位置插入1000個元素。ArrayList耗時約120ms,而LinkedList竟然用了2000ms。原因在于,LinkedList的遍歷需要逐個訪問節點,CPU緩存不友好,而ArrayList的內存是連續的,System.arraycopy()底層用內存拷貝,速度極快。所以結論是:哪怕是O(n)的操作,也要看常數項的大小,不能只看復雜度。”


?基礎知識擴展:

SparseArray ??

?1. 設計背景與核心優勢?

?為什么需要 SparseArray???
在 Android 開發中,頻繁使用 HashMap<Integer, Object> 會導致 ?內存浪費? 和 ?性能下降,因為 Integer 鍵會觸發 ?自動裝箱?(創建大量小對象),而 HashMap 的鏈表/紅黑樹結構也會帶來額外內存開銷。

?SparseArray 的優化點?:

  • ?避免自動裝箱?:直接使用 int 作為鍵,無需 Integer 對象。
  • ?內存緊湊?:基于兩個平行數組(int[] 鍵數組,Object[] 值數組),無鏈表結構。
  • ?二分查找優化?:鍵數組有序,查找時通過二分法實現(時間復雜度 O(log n))。
// 對比示例:內存占用差異
HashMap<Integer, String> map = new HashMap<>(); // 每個鍵值對占用約 32 字節
SparseArray<String> sparseArray = new SparseArray<>(); // 每個鍵值對占用約 16 字節

?2. 內部實現原理?

?數據結構?:

  • ?鍵數組(int[] mKeys)??:有序存儲所有鍵。
  • ?值數組(Object[] mValues)??:與鍵數組一一對應,存儲值或標記刪除(DELETED)。

?核心操作?:

  1. ?插入(put(int key, E value))??:

    • 通過二分查找確定插入位置。
    • 若位置已有數據且標記為 DELETED,直接覆蓋。
    • 若數組已滿,觸發擴容(當前容量 ≤4 則擴容到8,否則翻倍)。
  2. ?查找(get(int key))??:

    • 二分查找鍵數組,找到對應索引后返回值。
    • 未找到時返回默認值(可指定)。
  3. ?刪除(delete(int key))??:

    • 不立即縮容,僅將值標記為 DELETED,后續插入可復用位置。
    • 調用 gc() 方法時,清除所有 DELETED 標記并縮容。
// 源碼關鍵邏輯(簡化版)
public void put(int key, E value) {int i = binarySearch(mKeys, mSize, key); // 二分查找if (i >= 0) {mValues[i] = value; // 已存在則覆蓋} else {i = ~i; // 計算插入位置if (i < mSize && mValues[i] == DELETED) {mKeys[i] = key;mValues[i] = value;return;}// 擴容并插入新元素...}
}

?3. 性能對比與適用場景?

?**對比 HashMap<Integer, Object>**?:

特性SparseArrayHashMap
?鍵類型?intInteger
?內存占用?低(無對象頭、鏈表)高(自動裝箱+鏈表結構)
?查找性能?O(log n)O(1)
?插入/刪除性能?O(n)(需移動元素)O(1)(鏈表操作)
?適用數據量?小規模(百級以內)中大規模

?使用場景?:

  • ?鍵為 int 且數據量小?:如 R.id.xxx 視圖緩存、枚舉配置。
  • ?內存敏感場景?:如 RecyclerView 的 ViewHolder 緩存。
  • ?低頻修改、高頻查詢?:如全局配置參數。
// 實戰案例:優化 RecyclerView 的 ViewHolder 緩存
private SparseArray<ViewHolder> mCachedViews = new SparseArray<>();@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {ViewHolder holder = mCachedViews.get(viewType);if (holder == null) {holder = new ViewHolder(...);mCachedViews.put(viewType, holder);}return holder;
}

?4. 常見坑點與替代方案?

?坑點?:

  1. ?鍵必須有序?:插入無序鍵會破壞二分查找邏輯,需手動排序。
  2. ?插入性能差?:大規模數據插入時,移動數組元素開銷大。
  3. ?刪除不縮容?:頻繁刪除需手動調用 gc() 避免內存泄漏。

?替代方案?:

  • ?**ArrayMap**?:支持任意對象鍵,內存效率優于 HashMap。
  • ?**LongSparseArray**?:鍵為 long 類型的變種。
  • ?**androidx.collection.CircularArray**?:循環數組,適合隊列場景。

?5. 面試高頻問題?

?Q1:SparseArray 和 HashMap 的主要區別是什么???

  • ?鍵類型?:SparseArray 用 int 避免裝箱;HashMap 用 Object
  • ?內存占用?:SparseArray 更緊湊,適合小數據量。
  • ?查找性能?:SparseArray 是 O(log n),HashMap 是 O(1)。

?Q2:什么情況下該用 SparseArray???

  • 鍵為 int,數據量小(百級以內),且需要低內存開銷時。

?Q3:SparseArray 的刪除邏輯有什么優化???

  • 延遲刪除:標記為 DELETED,插入時可復用位置;手動 gc() 觸發縮容。

?Q4:SparseArray 的查找性能為什么是 O(log n)???

  • 基于有序數組的二分查找,每次比較后范圍減半。

?Array、ArrayList 與 LinkedList 的區別詳解?

?1. 基本概念?
  • ?Array(數組)??:

    • ?固定長度,初始化時需指定大小。
    • ?內存連續,支持快速隨機訪問(通過索引直接定位,時間復雜度 O(1))。
    • ?示例?:int[] arr = new int[10];
  • ?ArrayList(動態數組)??:

    • ?基于 Array 實現,可自動擴容。
    • ?默認初始容量為 10,擴容時新容量為舊容量的 1.5 倍。
    • ?示例?:List<String> list = new ArrayList<>();
  • ?LinkedList(雙向鏈表)??:

    • ?通過節點指針連接元素,每個節點存儲前驅和后繼的引用。
    • ?插入/刪除高效,但隨機訪問慢(需遍歷鏈表)。
    • ?示例?:List<Integer> linkedList = new LinkedList<>();

?2. 核心區別對比?
?特性??Array??ArrayList??LinkedList?
?數據結構?固定長度數組動態數組(自動擴容)雙向鏈表
?隨機訪問速度?O(1)(最快)O(1)O(n)(最慢)
?插入/刪除效率?O(n)(需移動元素)尾部插入:O(1)(均攤);
中間/頭部插入:O(n)
頭尾插入:O(1)
中間插入:O(n)(需遍歷)
?內存占用?最低(僅數據)中等(動態數組 + 擴容開銷)最高(每個節點額外存儲指針)
?適用場景?已知固定長度的數據高頻隨機訪問,數據量變化不大頻繁插入/刪除,無需隨機訪問

?3. ArrayList 的擴容機制與計算?
?擴容規則?:
  • ?初始容量?:默認 10。
  • ?擴容公式?:新容量 = 舊容量 + 舊容量 >> 1(即 1.5 倍)。
  • ?觸發條件?:當添加元素時,當前元素數超過容量。
?擴容示例?:

假設依次添加 100 個元素到 ArrayList

  1. 初始容量 10,添加第 11 個元素時,擴容到 15。
  2. 添加第 16 個元素時,擴容到 22。
  3. 后續擴容依次為 33 → 49 → 73 → 109。

?總擴容次數?:5 次(容量從 10 → 15 → 22 → 33 → 49 → 73 → 109)。
?元素拷貝次數?:每次擴容需將舊數組元素復制到新數組。

  • 10(初始) → 15:拷貝 10 次
  • 15 → 22:拷貝 15 次
  • 22 → 33:拷貝 22 次
  • 33 → 49:拷貝 33 次
  • 49 → 73:拷貝 49 次
  • 73 → 109:拷貝 73 次
    ?總拷貝次數?:10 + 15 + 22 + 33 + 49 + 73 = ?202 次
?性能優化?:
  • ?預分配容量?:若已知數據量較大,初始化時指定容量,避免多次擴容。
    List<Integer> list = new ArrayList<>(1000); // 直接分配 1000 容量

?4. 操作時間復雜度對比?
?操作??Array??ArrayList??LinkedList?
隨機訪問(get(i)O(1)O(1)O(n)
尾部插入(add(e)不支持均攤 O(1)O(1)
頭部插入(addFirst(e)不支持O(n)(需移動元素)O(1)
中間插入(add(i, e)不支持O(n)O(n)(需遍歷)
尾部刪除(removeLast()不支持O(1)O(1)
頭部刪除(removeFirst()不支持O(n)O(1)

?5. 實戰場景建議?
  • ?高頻隨機訪問?:如按索引讀取數據 → ?ArrayList
    // 讀取第 100 個元素
    String data = list.get(100); // O(1)
  • ?頻繁頭尾插入/刪除?:如實現隊列或棧 → ?LinkedList
    // 實現棧(后進先出)
    linkedList.addFirst("item1"); // O(1)
    linkedList.removeFirst();     // O(1)
  • ?內存敏感場景?:如嵌入式設備 → ?Array?(無額外內存開銷)。
    int[] sensorData = new int[1000]; // 固定長度,內存緊湊

?6. 性能陷阱與規避?
  • ?ArrayList 的中間插入?:
    • ?問題?:在索引 0 處插入元素需移動所有元素,效率低下。
    • ?優化?:若需頻繁中間操作,考慮使用 ?LinkedList? 或重新設計數據結構。
  • ?LinkedList 的隨機訪問?:
    • ?問題?:get(1000) 需從頭遍歷,效率極低。
    • ?優化?:改用 ?ArrayList? 或緩存常用節點。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82377.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82377.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82377.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

嵌入式學習之系統編程(五)進程(2)

一、進程的退出 &#xff08;一&#xff09;僵尸進程與孤兒進程 &#xff08;二&#xff09;相關函數 1、exit函數 2、_exit函數 3、atexit函數 二、進程空間的回收&#xff08;相關函數&#xff09; 1、wait函數 2、waitpid函數 3、練習 4、exec族 5、system函數 一…

AI時代新詞-Transformer架構:開啟AI新時代的關鍵技術

一、什么是Transformer架構&#xff1f; Transformer架構 是一種基于自注意力機制&#xff08;Self-Attention Mechanism&#xff09;的深度學習模型架構&#xff0c;最初由Vaswani等人在2017年的論文《Attention Is All You Need》中提出。它主要用于處理序列數據&#xff08…

基于cornerstone3D的dicom影像瀏覽器 第二十三章 mpr預設窗值與vr preset

文章目錄 前言一、mpr窗口預設窗值二、vr preset三、調用流程 前言 實現mpr窗口預設窗值&#xff0c;vr窗口預設配色 效果如下&#xff1a; 一、mpr窗口預設窗值 可參考 第十五章 預設窗值 邏輯一樣的&#xff0c;把windowWidth, windowCenter值轉換為voiRange值&#xff0c;…

shell之通配符及正則表達式

通配符與正則表達式 通配符&#xff08;Globbing&#xff09; 通配符是由 Shell 處理的特殊字符&#xff0c;用于路徑或文件名匹配。當 Shell 在命令參數中遇到通配符時&#xff0c;會將其擴展為匹配的文件路徑&#xff1b;若沒有匹配項&#xff0c;則作為普通字符傳遞給命令…

繼電保護與安全自動裝置:電力系統安全的守護神

電力系統是現代社會賴以生存的基礎設施&#xff0c;而繼電保護和安全自動裝置則是保障電力系統安全穩定運行的守護神。 它們默默無聞地工作著&#xff0c;在電力系統出現異常時&#xff0c;能夠迅速準確地切除故障&#xff0c;防止事故擴大&#xff0c;保障電力供應。 那么&…

Flink流處理基礎概論

文章目錄 引言Flink基本概述傳統數據架構的不足Dataflow中的幾大基本概念Dataflow流式處理宏觀流程數據并行和任務并行的區別Flink中幾種數據傳播策略Flink中事件的延遲和吞吐事件延遲事件的吞吐如何更好的理解事件的延遲和吞吐flink數據流的幾種操作輸入輸出轉換操作滾動聚合窗…

Tomcat 使用與配置全解

一、 Tomcat簡介 Tomcat服務器是Apache的一個開源免費的Web容器。它實現了JavaEE平臺下部分技術規范&#xff0c;屬于輕量級應用服務器。 1. Tomcat版本 Tomcat版本 JDK版本 Servlet版本 JSP版本 10.0.X 8 and later 5.0 3.0 9.0.x 8 and later 4.0 2.3 8.0.x 7…

Unity3D仿星露谷物語開發52之菜單頁面

1、目標 創建菜單頁面&#xff0c;可通過Esc鍵開啟或關閉。 當把鼠標懸停在上面時它會高亮&#xff0c;然后當點擊按鈕時標簽頁會被選擇。 2、 創建PauseMenuCanvas &#xff08;1&#xff09;創建Canvas 在Hierarchy -> PersistentScene -> UI下創建新的Cavans命名為…

Spring Boot 調優的 12 個關鍵節點

數據庫連接池調優&#xff1a;精準匹配系統資源 癥狀&#xff1a; 默認配置下&#xff0c;連接池資源使用不當&#xff0c;高并發時連接耗盡或排隊。 常見誤區&#xff1a; spring:datasource:hikari:maximum-pool-size: 1000 # 設置過大connection-timeout: 30000 # 設置…

前端流行框架Vue3教程:28. Vue應用

28. Vue應用 應用實例 每個 Vue 應用都是通過 createApp函數創建一個新的 應用實例 main.js import {createApp} from vue import App from ./App.vue// app:Vue的實例對象 // 在一個Vue項目中&#xff0c;有且只有一個Vue的實例對象 const app createApp(App)/* 根組件選項…

MongoDB 數據庫遷移:完整指南與最佳實踐

在現代數據驅動的應用中&#xff0c;數據庫遷移是一項常見的任務&#xff0c;無論是升級 MongoDB 版本、更換服務器硬件&#xff0c;還是遷移到云環境&#xff08;如 MongoDB Atlas&#xff09;&#xff0c;都需要一個可靠的遷移策略。錯誤的遷移方式可能導致數據丟失、應用停機…

MQTT-Vue整合

Vue整合 依賴環境 nodejs 版本 > 18安裝 element plus npm install element-plus安裝 mqtt npm install mqtt初始化Vue項目 使用 vite 創建項目 執行命令 npm create vitelatest輸入項目名稱 vue-mqtt-demo MQTT連接 連接組件代碼 components/MqttDemo.vue <script…

IP 地址反向解析(IP反查域名)原理與應用

一、IP 地址反向解析的原理與技術細節 IP 地址反向解析&#xff08;Reverse IP Lookup&#xff09;是一種將 IP 地址映射回其關聯域名或主機名的網絡技術&#xff0c;與常見的正向 DNS 解析&#xff08;將域名解析為 IP 地址&#xff09;形成互補。這一過程在網絡安全研究、漏…

Mermaid 文件支持的圖表

Mermaid 文件后綴支持多種類型的圖表&#xff0c;包括但不限于&#xff1a; 流程圖&#xff1a;用于描述流程和決策的圖表&#xff0c;常用于業務流程的表示和分析。 時序圖&#xff1a;用于描述事件發生的順序和時序關系的圖表&#xff0c;常用于系統交互和消息傳遞的分析。 …

用 Python 構建自動駕駛的實時通信系統:讓車輛“交流”起來!

用 Python 構建自動駕駛的實時通信系統:讓車輛“交流”起來! 自動駕駛技術正加速變革全球交通體系,它不僅是機器學習與計算機視覺的勝利,更是一場 高效通信架構的革命。自動駕駛汽車需要實時交換信息,比如: 傳感器數據(雷達、激光雷達、攝像頭)V2V(車與車通信)V2X(…

PDF處理控件Aspose.PDF教程:以編程方式合并PDF文檔

合并 PDF 文檔是常見的需求——無論您是整理報告、合并發票還是整合掃描頁面。單一、統一的文件更易于在個人、學術或專業用途中共享、存儲和管理。 本文將向您展示如何使用 Aspose.PDF在C#、Java 和 Python中以編程方式合并 PDf 文件。 Aspose.PDF最新版下載 為什么使用 As…

.gitignore 的基本用法

.gitignore 文件是 Git 版本控制系統中一個非常重要的配置文件&#xff0c;用于指定哪些文件或目錄應該被 Git 忽略&#xff0c;不納入版本控制。合理使用 .gitignore 可以避免將臨時文件、編譯產物、敏感信息等不必要的文件提交到代碼倉庫中。 1. .gitignore 的基本用法 &…

華為OD機試真題——分糖果(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳實現

2025 A卷 100分 題型 本專欄內全部題目均提供Java、python、JavaScript、C、C++、GO六種語言的最佳實現方式; 并且每種語言均涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、3個測試用例以及綜合分析; 本文收錄于專欄:《2025華為OD真題目錄+全流程解析+備考攻略+經驗分…

通過chrome插件自動生成博客評論,高效發外鏈

最近crazy cattle 3d這個詞爆火&#xff0c;很多人都在做&#xff0c;競爭異常激烈&#xff0c;甚至可以說是慘不忍睹。 從最近的數據看&#xff0c;勝出的主要是crazycattle3d.com, crazycattle3d.io, crazy-cattle-3d.com這幾個&#xff0c;流量最高的crazycattle3d.com已經…

創建一個簡易的風扇動畫界面:基于 WPF 和 XAML 的實現教程

在本教程中&#xff0c;我們將通過使用 WPF (Windows Presentation Foundation) 和 XAML (Extensible Application Markup Language) 創建一個簡單的“臺式風扇”界面。我們將使用 XAML 繪制風扇的外觀&#xff0c;包含風扇葉片、風扇框架、支架和按鈕等元素&#xff0c;并通過…