Java >> Java tutorial >  >> Tag >> HashMap

Konvertering af Java-egenskaber til HashMap

1. Introduktion

Mange udviklere beslutter at gemme applikationsparametre uden for kildekoden. En af måderne at gøre det på i Java er at bruge en ekstern konfigurationsfil og læse dem via java.util.Properties klasse.

I denne vejledning vil vi fokusere på forskellige metoder til at konvertere java.util.Properties ind i en HashMap . Vi implementerer forskellige metoder til at nå vores mål ved at bruge almindelig Java, lambdas eller eksterne biblioteker. Gennem eksempler vil vi diskutere fordele og ulemper ved hver løsning.

2. HashMap Konstruktør

Før vi implementerer vores første kode, lad os tjekke Javadoc for java.util.Properties . Som vi ser, arver denne hjælpeklasse fra Hashtable , som også implementerer Kort interface. Desuden omslutter Java sin Reader og Forfatter klasser til at arbejde direkte på String værdier.

Ifølge disse oplysninger kan vi konvertere Egenskaber ind i HashMap ved hjælp af typecasting og constructor-kald.

Forudsat at vi har indlæst vores Egenskaber korrekt, kan vi implementere:

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

Her implementerer vi vores konvertering i tre enkle trin.

For det første skal vi ifølge arvegrafen caste vores Egenskaber ind i et råt kort . Denne handling vil fremtvinge den første compiler-advarsel, som kan deaktiveres ved at bruge @SuppressWarnings(“rawtypes”) annotation.

Derefter støber vi vores rå kort ind i Map , hvilket forårsager en anden compiler-advarsel, som kan udelades ved at bruge @SupressWarnings(“unchecked”) .

Til sidst bygger vi vores HashMap ved hjælp af kopikonstruktøren. Dette er den hurtigste måde at konvertere vores Ejendomme på , men denne løsning har også en stor ulempe relateret til typesikkerhed :Vores Ejendomme kan være kompromitteret og ændret før konverteringen.

Ifølge dokumentationen er Egenskaberne klasse har setProperty() og getProperty() metoder, der tvinger brugen af ​​String værdier. Men der er også put() og putAll() metoder, der er arvet fra Hashtable der tillader brug af enhver type som nøgler eller værdier i vores Egenskaber :

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

Som vi kan se, udføres vores konvertering uden nogen fejl, men ikke alle elementer i HashMap er strenge. Så selvom denne metode ser den nemmeste ud, skal vi huske nogle sikkerhedsrelaterede kontroller i fremtiden.

3. Guava API

Hvis vi kan bruge tredjepartsbiblioteker, er Google Guava API praktisk. Dette bibliotek leverer en statisk Maps.fromProperties() metode, som gør næsten alt for os. Ifølge dokumentationen returnerer dette opkald et ImmutableMap , så hvis vi vil have HashMap, vi kan bruge:

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

Som tidligere denne metode fungerer fint, når vi er helt sikre på, at Egenskaberne indeholder kun streng værdier. At have nogle ikke-overensstemmende værdier vil føre til uventet adfærd:

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

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

Guava API udfører ikke yderligere tilknytninger. Som et resultat tillader det ikke os at konvertere disse egenskaber , med undtagelser.

I det første tilfælde er NullPointerException kastes på grund af heltal værdi, som ikke kan hentes af Egenskaberne. getProperty() metode og som følge heraf fortolkes som null . Det andet eksempel kaster ClassCastException relateret til ikke-strengen nøgle, der forekommer på input-egenskabskortet.

Denne løsning giver os bedre typekontrol og informerer os også om overtrædelser der opstår under konverteringsprocessen.

4. Custom Type Sikkerhedsimplementering

Det er nu tid til at løse vores type sikkerhedsproblemer fra de tidligere eksempler. Som vi ved, er Egenskaberne klasse implementerer metoder, der er arvet fra kortet interface. Vi vil bruge en af ​​de mulige måder at gentage over et Kort at implementere en ordentlig løsning og berige den med typetjek.

4.1. Iteration Brug til Løkke

Lad os implementere en simpel 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;
}

I denne metode gentager vi Egenskaberne på samme måde som vi gør for et typisk Kort . Som et resultat har vi en-for-en-adgang til hver enkelt nøgleparværdi repræsenteret af Map.Entry klasse.

Før du sætter værdier i et returneret HashMap , kan vi udføre yderligere kontroller, så vi beslutter at bruge String.valueOf() metode.

4.2. Stream og Samlere API

Vi kan endda refaktorisere vores metode ved at bruge den moderne Java 8-måde:

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

I dette tilfælde bruger vi Java 8 Stream Collectors uden eksplicit HashMap konstruktion. Denne metode implementerer nøjagtig den samme logik, som blev introduceret i det foregående eksempel.

Begge løsninger er lidt mere komplekse, fordi de kræver en vis tilpasset implementering at typecasting- og Guava-eksemplerne ikke gør det.

Vi har dog adgang til værdierne før du sætter dem på det resulterende HashMap , så vi kan implementere yderligere kontroller eller kortlægninger :

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

Som vi kan se, løste vi vores problemer relateret til ikke-strengværdier. Ved at bruge denne tilgang kan vi manuelt justere kortlægningslogikken for at opnå korrekt implementering.

5. Konklusion

I dette selvstudie har vi tjekket forskellige metoder til at konvertere java.util.Properties ind i en HashMap .

Vi startede med en typecasting-løsning, der måske er den hurtigste konvertering, men som også bringer compileradvarsler og potentielle typesikkerhedsfejl .

Derefter tog vi et kig på en løsning, der bruger Guava API, som løser kompileringsadvarsler og bringer nogle forbedringer til håndtering af fejl.

Endelig implementerede vi vores brugerdefinerede metoder, som håndterer typesikkerhedsfejl og giver os mest kontrol.

Alle kodestykker fra denne øvelse er tilgængelige på GitHub.


Java tag