1. 序章

多くの開発者は、アプリケーションパラメータをソースコードの外部に保存することを決定します。 Javaでこれを行う方法の1つは、外部構成ファイルを使用して、java.util.Propertiesクラスを介してそれらを読み取ることです。

このチュートリアルでは、焦点を当てます java.util.PropertiesをHashMapに変換するためのさまざまなアプローチ 。 プレーンなJava、 lambdas 、または外部ライブラリを使用して、目標を達成するためにさまざまなメソッドを実装します。 例を通して、各ソリューションの長所と短所について説明します。

2. HashMapコンストラクター

最初のコードを実装する前に、Javaドキュメントでjava.util.Propertiesを確認しましょう。 ご覧のとおり、このユーティリティクラスはハッシュ表 、これも実装します地図インターフェース。 さらに、JavaはReaderクラスとWriterクラスをラップして、String値を直接処理します。

その情報によると、私たちは変換することができますプロパティの中へ HashMap 型キャストとコンストラクター呼び出しを使用します。

プロパティを正しくロードしたと仮定すると、次のように実装できます。

public static HashMap<String, String> typeCastConvert(Properties prop) {
    Map step1 = prop;
    Map<String, String> step2 = (Map<String, String>) step1;
    return new HashMap<>(step2);
}

ここでは、3つの簡単なステップで変換を実装します。

まず、継承グラフに従って、プロパティを生のマップにキャストする必要があります。 このアクションにより、最初のコンパイラ警告が強制されます。これは、 @SuppressWarnings( “rawtypes”)アノテーションを使用して無効にできます。

その後、生をキャストします地図の中へ地図 、別のコンパイラ警告が発生します。これは、を使用して省略できます。 @SupressWarnings(“ unchecked”)

最後に、コピーコンストラクターを使用してHashMapをビルドします。 これはプロパティを変換する最速の方法ですが、このソリューションにはタイプセーフティに関連する大きな欠点もあります。変換前にプロパティが危険にさらされて変更される可能性があります。

ドキュメントによると、 Properties クラスには、 String値の使用を強制するsetProperty()および getProperty()メソッドがあります。 ただし、 Hashtableから継承されたput()メソッドと putAll()メソッドもあり、プロパティで任意のタイプをキーまたは値として使用できます。 ]:

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 GuavaAPIが便利です。 このライブラリは、静的な Maps.fromProperties()メソッドを提供します。このメソッドは、ほとんどすべてを実行します。 ドキュメントによると、この呼び出しは ImmutableMap を返すため、 HashMap、が必要な場合は、次を使用できます。

public HashMap<String, String> guavaConvert(Properties prop) {
    return Maps.newHashMap(Maps.fromProperties(prop));
}

以前のように、 この方法プロパティに文字列値のみが含まれていることが完全に確認されている場合は、正常に機能します。 不適合な値があると、予期しない動作が発生します。

properties.put("property4", 456);
assertThrows(NullPointerException.class, 
    () -> PropertiesToHashMapConverter.guavaConvert(properties));

properties.put(5, 10.11);
assertThrows(ClassCastException.class, 
    () -> PropertiesToHashMapConverter.guavaConvert(properties));

Guava APIは、追加のマッピングを実行しません。 その結果、これらのプロパティを変換できず、例外がスローされます。

最初のケースでは、 Integer 値が原因で、 NullPointerException がスローされます。この値は、Properties。 getProperty()では取得できません。メソッドであり、その結果、nullとして解釈されます。 2番目の例では、入力プロパティマップで発生する文字列以外のキーに関連するClassCastExceptionをスローします。

このソリューションは、より優れた型制御を提供し、違反を通知します 変換プロセス中に発生します。

4. カスタム型安全性の実装

今度は、前の例から私たちのタイプの安全性の問題を解決する時が来ました。 ご存知のように、 Properties クラスは、Mapインターフェースから継承されたメソッドを実装します。 マップ上で反復する可能な方法の1つを使用して、適切なソリューションを実装し、タイプチェックで強化します。

4.1. ループにを使用した反復

単純なforループを実装しましょう。

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クラスで表されるすべてのキーペア値に1つずつアクセスできます。

返されたHashMapに値を入れる前に、追加のチェックを実行できるため、 String.valueOf()メソッドを使用することにしました。

4.2. ストリームおよびコレクターAPI

最新のJava8の方法を使用して、メソッドをリファクタリングすることもできます。

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
    ));
}

この場合、明示的な HashMap 構造なしで、Java8ストリームコレクターを使用しています。 このメソッドは、前の例で紹介したのとまったく同じロジックを実装します。

どちらのソリューションも、型キャストや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

おそらく最速の変換であるが、コンパイラの警告と潜在的な型安全性エラーをもたらす型キャストソリューションから始めました。

次に、Guava APIを使用したソリューションを検討しました。これにより、コンパイラの警告が解決され、エラーの処理が改善されます。

最後に、型安全性エラーを処理し、最大限の制御を可能にするカスタムメソッドを実装しました。

このチュートリアルのすべてのコードスニペットは、GitHubから入手できます。