OkHttp 手動解壓縮 gzip/deflate 響應
大多數時候,OkHttp 會自動解壓 gzip 響應。但某些老舊 server 或特殊場景,response 的 Content-Encoding 是 gzip/deflate,但 OkHttp 沒有自動幫你解。這時就要自己手動解壓。 問題場景通常是這些情況: Server 回傳 Content-Encoding: gzip,但沒有設定好 headers 某些代理或中間層加了壓縮但沒遵循標準 特殊的嵌入式系統的 API 需要在 interceptor 層級處理壓縮和解壓 實作 UnzippingInterceptor直接上完整代碼: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576import okhttp3.Interceptor;import okhttp3.Response;import okio.BufferedSource...
Jackson JSON 使用技巧 - JsonNode 遍歷與 path()
Jackson 是 Java 裡最強的 JSON 庫,但新手常搞不清楚 JsonNode 的各種遍歷方法,還有 path() 和 get() 的差別。踩過坑以後,整理出來給大家。 JsonNode 遍歷拿到一個 JsonNode,要怎麼遍歷裡面的欄位?有好幾種方法。 方法 1:用 fields() 取得 Iterator12345678910111213141516171819202122232425ObjectMapper mapper = new ObjectMapper();String json = """{ "name": "Alice", "age": 30, "email": "alice@example.com"}""";JsonNode node = mapper.readTree(json);// fields() 回傳 Iterator<Map.Entry&...
Java Stream 操作集錦 - 合併 Map、List 轉 Map、物件排序
Stream 操作是 Java 8+ 的大招。三個最常用的場景:合併多個 Map、把 List 轉成 Map、排序物件。一次整理出來,以後就 copy-paste 吧。 1. Stream 合併多個 Map經常要把好幾個 Map 合起來,最直白的做法是逐個 put: 123456789101112131415Map<String, Integer> map1 = new HashMap<>();map1.put("Apple", 10);map1.put("Banana", 20);Map<String, Integer> map2 = new HashMap<>();map2.put("Orange", 15);map2.put("Pear", 25);Map<String, Integer> map3 = new HashMap<>();map3.put("Cherry", 30);// 爛做法Map<...
Java Stream 過濾 List<JSON> 重複資料
常常拿到一堆 JSON 格式的資料,裡面有一大堆重複的垃圾要清。用 List 直接 remove 重複項,會爆炸。用 Stream 搭配 Collectors,一行代碼搞定,超爽。 問題場景比如從 API 拿到的優惠券列表,user_create_time 相同的算是重複: 123456[ {"id": "C001", "user_create_time": "2026-01-01 10:00:00", "discount": "10%"}, {"id": "C002", "user_create_time": "2026-01-01 10:00:00", "discount": "10%"}, {"id": "C003", "user_c...
淺談 Hash、Hashtable 與 HashMap
Hash 這玩意兒是程式世界的基礎,但一堆人搞不清楚 Hash、Hashtable、HashMap 的差別。常見情況:被問到為啥用 HashMap 不用 Hashtable,只能兩眼發直。今天就一次講清楚。 Hash 的概念Hash 就是把任意長度的資料「打碎」再「拼湊」成固定長度的代碼。 Hash Function 做的事12String input = "This is a very long sentence with lots of words";int hashCode = input.hashCode(); // 回傳一個整數,比如 -123456789 Hash Function 的特性: 決定性 - 同一個輸入永遠產生同一個輸出 單向性 - 不可逆,不能從 hash code 推回原始資料 雪崩效應 - 輸入改一點點,輸出就完全不同 均勻分佈 - 理想情況下,不同輸入的 hash 值均勻分佈在整個範圍 Hash 的應用1. 數位簽章確認資料沒有被竄改。簽署人計算文件的 hash 值,用私鑰加密。驗證人計算一遍 hash,再用公鑰解密比對...
Java Retry 重試機制 - 指數退避模版
遠端呼叫爆了,重試一下通常就活了。但如果一直狂轟,server 只會更爆。所以咱們要聰明一點,用指數退避(exponential backoff)的方式慢慢來。 為啥要指數退避直接 retry 沒有 delay 的問題: Server 還沒回過來,馬上再打一次,等於雪上加霜 頻繁重試反而加重服務負擔 看起來像 DDoS 攻擊 XD 指數退避的邏輯: 第 1 次失敗,等 2 秒再試 第 2 次失敗,等 4 秒再試 第 3 次失敗,等 8 秒再試 以此類推… 給 server 喘息的時間,機率就高很多。 通用模版12345678910111213141516171819202122232425262728public boolean retry(int maxAttempts, int initialDelaySeconds, Runnable action) { int attempts = 0; int delay = initialDelaySeconds; while (attempts < maxAttempts) ...
Java Optional 完整用法指南
Optional 是 Java 8 引入的概念,初看很簡單,其實用法超深。一開始很多人覺得 Optional 很煩,後來用對了才發現「真香」。最關鍵是理解它的三段式結構。 Optional 的三段式結構Optional 的用法可以分成三段:頭部(建立)→ 身體(轉換)→ 尾巴(取值) 12建立 轉換(0~多個) 取值Optional → filter/map/flatMap → orElse/ifPresent 頭部:建立 Optionalof() - 確定非 null1234567User user = new User("Alice");// 用 of() 當你確定值一定不為 nullOptional<User> opt = Optional.of(user);// 如果值是 null,直接拋 NullPointerExceptionOptional<User> opt2 = Optional.of(null); // ❌ 拋 exception ofNullable() - 可能 null12345...
Java 實體類物件比較 - equals 與 hashCode 正確實作
比較兩個物件相等這種事,看似簡單但超容易踩坑。特別是涉及 equals 和 hashCode 時,小細節搞錯就會爆。見過太多人只覆寫 equals 不覆寫 hashCode,或用 BigDecimal 的 equals 爆掉的慘案。 核心概念要比較兩個物件相等,**必須同時覆寫 equals() 和 hashCode()**。 為什麼?因為 HashMap、HashSet 等容器依賴 hashCode 和 equals 配合: 123456789// 容器先用 hashCode 定位,再用 equals 驗證Map<User, String> map = new HashMap<>();User u1 = new User(1L, "Alice");map.put(u1, "value1");User u2 = new User(1L, "Alice");// 如果只覆寫 equals 不覆寫 hashCode:// u1 和 u2 hashCode 不同 → 容器認為是不同的鍵// map.ge...
HashMap 轉 JSON 的三種方式
HashMap 轉 JSON 看似簡單,但真正用上去踩坑才多。有三種常見的方式,各有優缺點,選對一個才不會被 JsonProcessingException 折磨。 場景說明先假設有這樣的 HashMap: 1234567891011Map<String, Object> userMap = new HashMap<>();userMap.put("id", 123);userMap.put("name", "Alice");userMap.put("email", "alice@example.com");userMap.put("age", 30);userMap.put("tags", Arrays.asList("admin", "developer"));Map<String, Object> addressMap = new HashMap<>...
Java 8 Stream forEach 與傳統迭代的差異
一開始用 Stream 時,就是 forEach 走天下。後來才發現 Stream forEach 和傳統 Iterator 根本是兩套邏輯,搞混的話效能會爆。 概念差異傳統迭代:外部迭代(External Iteration)你決定怎麼走訪: 1234567891011121314151617List<String> names = Arrays.asList("Alice", "Bob", "Charlie");// for-loop 迴圈for (int i = 0; i < names.size(); i++) { System.out.println(names.get(i));}// Iterator 迴圈Iterator<String> iterator = names.iterator();while (iterator.hasNext()) { System.out.println(iterator.next());}//...














