將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包裝了其ReaderWriter類,以直接在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 PropertiesMap.Entry類表示的每個單個鍵對值進行一對一訪問。

    在將值放入返回的HashMap ,我們可以執行其他檢查,因此我們決定使用String.valueOf()方法。

    4.2。 StreamCollectors 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的解決方案,該解決方案解決了編譯器警告並為處理錯誤提供了一些改進。

    最後,我們實現了自定義方法,該方法處理類型安全性錯誤並提供最大的控制權。