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

Java HashMap med forskellige værdityper

1. Oversigt

Et HashMap gemmer nøgleværdikortlægninger. I denne øvelse vil vi diskutere, hvordan man gemmer værdier af forskellige typer i et HashMap .

2. Introduktion til problemet

Siden introduktionen af ​​Java Generics har vi typisk brugt HashMap på en generisk måde – for eksempel:

Map<String, Integer> numberByName = new HashMap<>();

I dette tilfælde kan vi kun sætte String og Heltal data som nøgleværdi-par i kortet numberByName . Det er godt, da det sikrer typesikkerhed. For eksempel, hvis vi forsøger at sætte en Float objekt på kortet , får vi kompileringsfejlen "inkompatible typer".

Men nogle gange vil vi gerne indsætte data af forskellige typer på et kort . For eksempel vil vi have numberByName kort for at gemme Float og BigDecimal objekter som værdier også.

Før vi diskuterer, hvordan man opnår det, lad os lave et eksempelproblem for at gøre demonstrationen og forklaringen nemmere. Lad os sige, at vi har tre objekter af forskellige typer:

Integer intValue = 777;
int[] intArray = new int[]{2, 3, 5, 7, 11, 13};
Instant instant = Instant.now();

Som vi kan se, er de tre typer helt forskellige. Så først vil vi prøve at placere disse tre objekter i et HashMap . For at gøre det enkelt bruger vi String værdier som nøgler.

Selvfølgelig skal vi på et tidspunkt læse dataene ud fra kortet og bruge dataene. Derfor vil vi gennemgå posterne i HashMap , og for hver post udskriver vi værdien med en beskrivelse.

Så lad os se, hvordan vi kan opnå det.

3. Brug Map

Vi ved, at Object i Java er supertypen af ​​alle typer . Derfor, hvis vi erklærer et Kort som Kort , bør den acceptere værdier af enhver type.

Lad os derefter se, om denne tilgang opfylder vores krav.

3.1. Indsættelse af data på kortet

Som vi har nævnt tidligere, er et Map giver os mulighed for at sætte værdier af enhver type i den:

Map<String, Object> rawMap = new HashMap<>();
rawMap.put("E1 (Integer)", intValue);
rawMap.put("E2 (IntArray)", intArray);
rawMap.put("E3 (Instant)", instant);

Det er ret ligetil. Lad os derefter besøge posterne på kortet og udskriv værdien og beskrivelsen.

3.2. Brug af dataene

Efter at vi har sat en værdi i Map , vi har mistet værdiens betontype. Derfor skal vi kontrollere og caste værdien til den rigtige type, før vi bruger dataene . For eksempel kan vi bruge instanceof operatør for at bekræfte en værdis type:

rawMap.forEach((k, v) -> {
    if (v instanceof Integer) {
        Integer theV = (Integer) v;
        System.out.println(k + " -> "
          + String.format("The value is a %s integer: %d", theV > 0 ? "positive" : "negative", theV));
    } else if (v instanceof int[]) {
        int[] theV = (int[]) v;
        System.out.println(k + " -> "
          + String.format("The value is an array of %d integers: %s", theV.length, Arrays.toString(theV)));
    } else if (v instanceof Instant) {
        Instant theV = (Instant) v;
        System.out.println(k + " -> "
          + String.format("The value is an instant: %s", FORMATTER.format(theV)));
    } else {
        throw new IllegalStateException("Unknown Type Found.");
    }
});

Hvis vi udfører koden ovenfor, vil vi se outputtet:

E1 (Integer) -> The value is a positive integer: 777
E2 (IntArray) -> The value is an array of 6 integers: [2, 3, 5, 7, 11, 13]
E3 (Instant) -> The value is an instant: 2021-11-23 21:48:02

Denne tilgang fungerer som vi forventede.

Det har dog nogle ulemper. Lad os derefter se nærmere på dem.

3.3. Ulemper

For det første, hvis vi har planlagt at lade kortet understøtte relativt flere forskellige typer, de mange if-else sætninger bliver en stor kodeblok og gør koden svær at læse .

Desuden, hvis de typer, vi ønsker at bruge, indeholder arveforhold, vil instansen af check kan mislykkes .

For eksempel, hvis vi sætter en java.lang.Integer intValue og en java.lang.Number numberValue på kortet kan vi ikke skelne dem ved hjælp af instansen af operatør. Dette skyldes, at både (intValue-forekomst af heltal) og (intValue instanceof Number) returner sand .

Derfor skal vi tilføje ekstra kontroller for at bestemme en værdis konkrete type. Men det vil selvfølgelig gøre koden svær at læse.

Endelig, da vores kort accepterer værdier af enhver type, har vi mistet typesikkerheden . Det vil sige, vi skal håndtere undtagelsen, når der stødes på uventede typer.

Et spørgsmål kan dukke op:Er der en måde at acceptere forskellige typers data og bevare typesikkerheden?

Så næste gang vil vi behandle en anden tilgang til at løse problemet.

4. Oprettelse af en supertype for alle påkrævede typer

I dette afsnit introducerer vi en supertype for at bevare typesikkerheden.

4.1. Datamodel

Først opretter vi en grænseflade DynamicTypeValue :

public interface DynamicTypeValue {
    String valueDescription();
}

Denne grænseflade vil være supertypen af ​​alle typer, som vi forventer, at kortet understøtter . Det kan også indeholde nogle almindelige operationer. For eksempel har vi defineret en metode valueDescription .

Derefteropretter vi en klasse for hver konkret type for at indpakke værdien og implementere grænsefladen vi har skabt. For eksempel kan vi oprette en IntegerTypeValue klasse for Heltal type:

public class IntegerTypeValue implements DynamicTypeValue {
    private Integer value;

    public IntegerTypeValue(Integer value) {
        this.value = value;
    }

    @Override
    public String valueDescription() {
        if(value == null){
            return "The value is null.";
        }
        return String.format("The value is a %s integer: %d", value > 0 ? "positive" : "negative", value);
    }
}

På samme måde, lad os oprette klasser for de to andre typer:

public class IntArrayTypeValue implements DynamicTypeValue {
    private int[] value;

    public IntArrayTypeValue(int[] value) { ... }

    @Override
    public String valueDescription() {
        // null handling omitted
        return String.format("The value is an array of %d integers: %s", value.length, Arrays.toString(value));
    }
}
public class InstantTypeValue implements DynamicTypeValue {
    private static DateTimeFormatter FORMATTER = ...

    private Instant value;

    public InstantTypeValue(Instant value) { ... }

    @Override
    public String valueDescription() {
        // null handling omitted
        return String.format("The value is an instant: %s", FORMATTER.format(value));
    }
}

Hvis vi skal understøtte flere typer, tilføjer vi bare tilsvarende klasser.

Lad os derefter se på, hvordan du bruger datamodellen ovenfor til at gemme og bruge forskellige typers værdier på et kort.

4.2. Anbring og brug af data på kortet

Lad os først se, hvordan du erklærer kortet og læg forskellige typer data i den:

Map<String, DynamicTypeValue> theMap = new HashMap<>();
theMap.put("E1 (Integer)", new IntegerTypeValue(intValue));
theMap.put("E2 (IntArray)", new IntArrayTypeValue(intArray));
theMap.put("E3 (Instant)", new InstantTypeValue(instant));

Som vi kan se, har vi erklæret kortet som Map typesikkerheden er garanteret :Kun data med DynamicTypeValue type er tilladt at blive sat ind på kortet.

Når vi tilføjer data til kortet, instansierer vi den tilsvarende klasse, vi har oprettet .

Når vi bruger dataene, er typekontrol og casting ikke påkrævet :

theMap.forEach((k, v) -> System.out.println(k + " -> " + v.valueDescription()));

Hvis vi kører koden, udskriver den:

E1 (Integer) -> The value is a positive integer: 777
E2 (IntArray) -> The value is an array of 5 integers: [2, 3, 5, 7, 11]
E3 (Instant) -> The value is an instant: 2021-11-23 22:32:43

Som vi kan se, er koden for denne tilgang ren og meget lettere at læse .

Da vi desuden opretter en indpakningsklasse for hver type, vi skal understøtte, vil typer med arveforhold ikke føre til noget problem.

Takket være typesikkerheden behøver vi ikke at håndtere fejlsagen af at stå over for data af uventede typer.

5. Konklusion

I denne artikel har vi diskuteret, hvordan man laver et Java HashMap understøtte forskellige typer værdidata.

Vi har også behandlet to tilgange til at opnå det gennem eksempler.

Som altid er kildekoden, der følger med artiklen, tilgængelig på GitHub.


Java tag