將java.util.Properties轉換為HashMap
1.簡介
許多開發人員決定將應用程序參數存儲在源代碼之外。在Java中,這樣做的方法之一是使用外部配置文件,並通過java.util.Properties
類讀取它們。
在本教程中,我們將重點介紹**java.util.Properties
轉換為HashMap<String, String>
各種方法**。我們將使用純Java,lambda或外部庫實現不同的方法來實現我們的目標。通過示例,我們將討論每種解決方案的利弊。
2. HashMap
構造函數
在實現第一個代碼之前,讓我們檢查一下Javadoc中的java.util.Properties
。如我們所見,該實用程序類繼承自Hashtable<Object, Object>
,後者也實現了Map
接口。而且,Java包裝了其Reader
和Writer
類,以直接在String
值上工作。
根據這些信息,我們可以使用類型轉換和構造函數調用Properties
轉換為HashMap<String, String>
假設我們已Properties
,則可以實現:
public static HashMap<String, String> typeCastConvert(Properties prop) {
Map step1 = prop;
Map<String, String> step2 = (Map<String, String>) step1;
return new HashMap<>(step2);
}
在這裡,我們通過三個簡單的步驟來實現我們的轉換。
首先,根據繼承圖,我們需要將Properties
轉換為原始Map
。此操作將強制執行第一個編譯器警告,可以使用@SuppressWarnings(“rawtypes”)
批註將其禁用。
之後,我們將原始Map
轉換為Map<String, String>
,從而引起另一個編譯器警告,可以通過使用@SupressWarnings(“unchecked”)
忽略該警告。
最後,我們使用copy構造函數HashMap
。這是**Properties
的最快方法,但是此解決方案還具有與類型安全有關的很大缺點**:轉換前,我們的Properties
可能會受到損害和修改。
根據文檔, Properties
類具有setProperty()
和getProperty()
方法,這些方法強制使用String
值。但是還有Hashtable
繼承的put()
和putAll()
Properties
中將任何類型用作鍵或值:
properties.put("property4", 456);
properties.put(5, 10.11);
HashMap<String, String> hMap = typeCastConvert(properties);
assertThrows(ClassCastException.class, () -> {
String s = hMap.get("property4");
});
assertEquals(Integer.class, ((Object) hMap.get("property4")).getClass());
assertThrows(ClassCastException.class, () -> {
String s = hMap.get(5);
});
assertEquals(Double.class, ((Object) hMap.get(5)).getClass());
如我們所見,我們的轉換執行沒有任何錯誤,但是HashMap
中的.
因此,即使此方法看起來最簡單,我們將來也必須牢記一些與安全相關的檢查。
3. Guava API
如果我們可以使用第三方庫,則可以使用Google Guava API。該庫提供了一個靜態[Maps.fromProperties()](https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/Maps.html#fromProperties-java.util.Properties-)
方法,該方法幾乎可以為我們做所有事情。根據文檔,此調用返回一個ImmutableMap
,因此,如果我們要HashMap,
則可以使用:
public HashMap<String, String> guavaConvert(Properties prop) {
return Maps.newHashMap(Maps.fromProperties(prop));
}
如前所述,當我們完全確定Properties
僅包含String
值**時,此方法可以正常工作。**某些不合格的值將導致意外的行為:
properties.put("property4", 456);
assertThrows(NullPointerException.class,
() -> PropertiesToHashMapConverter.guavaConvert(properties));
properties.put(5, 10.11);
assertThrows(ClassCastException.class,
() -> PropertiesToHashMapConverter.guavaConvert(properties));
Guava API不會執行任何其他映射。結果,它不允許我們轉換那些Properties
引發異常。
在第一種情況下, Integer
值而引發NullPointerException
,無法通過Properties.
getProperty()
方法,因此被解釋為null
。第二個示例拋出與輸入屬性映射上發生的非字符串ClassCastException
此解決方案為我們提供了更好的類型控制,並且還向我們通知**了轉換過程中發生的違規情況。**
4.自定義類型安全實施
現在是時候根據前面的示例解決類型安全問題了。眾所周知, Properties
類實現了從Map
接口繼承的方法。我們將使用一種迭代Map
的可能方式來實現適當的解決方案,並通過類型檢查來豐富它。
4.1。 for
循環迭代
讓我們實現一個簡單的for
-loop:
public HashMap<String, String> loopConvert(Properties prop) {
HashMap<String, String> retMap = new HashMap<>();
for (Map.Entry<Object, Object> entry : prop.entrySet()) {
retMap.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
}
return retMap;
}
在此方法中,我們以與對典型Map
Properties
。 Map.Entry
類表示的每個單個鍵對值進行一對一訪問。
在將值放入返回的HashMap
,我們可以執行其他檢查,因此我們決定使用String.valueOf()
方法。
4.2。 Stream
和Collectors
API
我們甚至可以使用現代Java 8方式重構我們的方法:
public HashMap<String, String> streamConvert(Properties prop) {
return prop.entrySet().stream().collect(
Collectors.toMap(
e -> String.valueOf(e.getKey()),
e -> String.valueOf(e.getValue()),
prev, next) -> next, HashMap::new
));
}
在這種情況下,我們使用的Java 8 Stream Collector沒有顯式的HashMap
構造。此方法實現與上一個示例完全相同的邏輯。
兩種解決方案都稍微複雜些,因為它們需要一些自定義實現,而typecasting和Guava示例則不需要。
但是,我們可以在將值放在生成的HashMap
上之前訪問它們,因此我們可以實現其他檢查或映射:
properties.put("property4", 456);
properties.put(5, 10.11);
HashMap<String, String> hMap1 = loopConvert(properties);
HashMap<String, String> hMap2 = streamConvert(properties);
assertDoesNotThrow(() -> {
String s1 = hMap1.get("property4");
String s2 = hMap2.get("property4");
});
assertEquals("456", hMap1.get("property4"));
assertEquals("456", hMap2.get("property4"));
assertDoesNotThrow(() -> {
String s1 = hMap1.get("property4");
String s2 = hMap2.get("property4");
});
assertEquals("10.11", hMap1.get("5"));
assertEquals("10.11", hMap2.get("5"));
assertEquals(hMap2, hMap1);
如我們所見,我們解決了與非字符串值有關的問題。使用這種方法,我們可以手動調整映射邏輯以實現適當的實現。
5.結論
在本教程中,我們檢查了將java.util.Properties
轉換為HashMap<String, String>
不同方法。
我們從類型轉換解決方案開始,這也許是最快的轉換,但也帶來了編譯器警告和潛在的類型安全錯誤。
然後,我們看了一個使用Guava API的解決方案,該解決方案解決了編譯器警告並為處理錯誤提供了一些改進。
最後,我們實現了自定義方法,該方法處理類型安全性錯誤並提供最大的控制權。