轉換地圖<String, Object>地圖<String, String>在爪哇
一、概述
在 Java 中,我們經常使用映射等集合來存儲鍵值對。
在本快速教程中,我們將探索將Map<String, Object>
轉換為Map<String, String>.
二、問題簡介
首先,讓我們創建一個Map<String, Object>
:
static final Map<String, Object> MAP1 = Maps.newHashMap();
static {
MAP1.put("K01", "GNU Linux");
MAP1.put("K02", "Mac OS");
MAP1.put("K03", "MS Windows");
}
因此,如果我們將其轉換為Map<String, String>
,結果應如下所示:
static final Map<String, String> EXPECTED_MAP1 = Maps.newHashMap();
static {
EXPECTED_MAP1.put("K01", "GNU Linux");
EXPECTED_MAP1.put("K02", "Mac OS");
EXPECTED_MAP1.put("K03", "MS Windows");
}
使用HashMap(Map<? extends K, ? extends V> m)
構造函數可能是完成轉換的第一個想法。那麼,讓我們試一試:
Map<String,String> result = new HashMap<String,String>(MAP1);
不幸的是,上面的行沒有編譯:
no suitable constructor found for HashMap(java.util.Map<java.lang.String,java.lang.Object>)
在本教程中,我們將討論解決問題的不同方法。
另外,正如我們所見,儘管MAP1
的類型是Map<String, Object>
,但所有條目的值都是字符串。由於值的類型參數是Object
,我們的輸入映射可能包含值不是字符串的條目:
static final Map<String, Object> MAP2 = Maps.newHashMap();
static {
MAP2.put("K01", "GNU Linux");
MAP2.put("K02", "Mac OS");
MAP2.put("K03", BigDecimal.ONE); // value is not a string
}
我們也將介紹此轉換方案。
為簡單起見,讓我們將MAP1
和MAP2
作為輸入並使用單元測試斷言來驗證每個解決方案是否按預期工作。
3.投射到Map
(不安全)
通用類型是一個編譯時特性。在運行時,所有類型參數都被擦除,所有映射都具有相同的類型Map<Object, Object>
。因此,我們可以將MAP1
轉換為原始Map
並將其分配給Map<String, String>
變量:
Map<String, String> result = (Map) MAP1;
assertEquals(EXPECTED_MAP1, result);
儘管在編譯時有一個“Unchecked Conversion”警告,但這個技巧還是有效的。
但是,這並不安全。例如,如果某些條目的值不是字符串,則此方法會隱藏問題:
Map<String, String> result2 = (Map) MAP2;
assertFalse(result2.get("K03") instanceof String);
如上面的測試所示,儘管result2
的類型為Map<String, String>
,但映射中的一個值不是字符串。這可能會導致進一步流程中出現意外問題。
那麼接下來,讓我們看看如何實現安全轉換。
4. 創建checkAndTransform()
方法
我們可以構建一個checkAndTransform()
方法來執行安全轉換:
Map<String, String> checkAndTransform(Map<String, Object> inputMap) {
Map<String, String> result = new HashMap<>();
for (Map.Entry<String, Object> entry : inputMap.entrySet()) {
try {
result.put(entry.getKey(), (String) entry.getValue());
} catch (ClassCastException e) {
throw e; // or a required error handling
}
}
return result;
}
如上面的方法所示,我們遍歷輸入映射中的條目,將每個條目的值轉換為String
,並將鍵值對放入新的Map<String, String>
。
讓我們用我們的MAP1
測試它:
Map<String, String> result = checkAndTransform(MAP1);
assertEquals(EXPECTED_MAP1, result);
此外,一旦輸入映射包含一些無法轉換為String
值,就會拋出ClassCastException
。讓我們將MAP2
傳遞給要測試的方法:
assertThrows(ClassCastException.class, () -> checkAndTransform(MAP2));
為簡單起見,在catch
塊中,我們throw
異常。根據需要,我們可以在捕獲到ClassCastException.
5. 將所有值轉換為字符串
有時,即使映射包含無法轉換為String
值,我們也不想捕獲任何異常。相反,對於那些“非字符串”值,我們希望將對象的String
表示形式作為值。例如,我們要將MAP2
轉換為這張地圖:
static final Map<String, String> EXPECTED_MAP2_STRING_VALUES = Maps.newHashMap();
static {
EXPECTED_MAP2_STRING_VALUES.put("K01", "GNU Linux");
EXPECTED_MAP2_STRING_VALUES.put("K02", "Mac OS");
EXPECTED_MAP2_STRING_VALUES.put("K03", "1"); // string representation of BigDecimal.ONE
}
讓我們使用 Stream API 來實現它:
Map<String, String> result = MAP1.entrySet()
.stream()
.collect(toMap(Map.Entry::getKey, e -> String.valueOf(e.getValue())));
assertEquals(EXPECTED_MAP1, result);
Map<String, String> result2 = MAP2.entrySet()
.stream()
.collect(toMap(Map.Entry::getKey, e -> String.valueOf(e.getValue())));
assertEquals(EXPECTED_MAP2_STRING_VALUES, result2);
值得一提的是,我們使用String.valueOf()
而不是e.toString()
來避免潛在的NullPointerException
。
六,結論
在本文中,我們了解了將Map<String, Object>
轉換為Map<String, String>.
此外,我們還討論了輸入映射包含非字符串值的情況。
與往常一樣,此處提供的所有代碼片段都可以在 GitHub 上找到。