There are various ways to achieve this. Here are three.

    Map<String, String> map = new HashMap<String, String>();
    map.put("key1", "value1");
    map.put("key2", "value2");
    map.put("key3", "value3");

    System.out.println("using entrySet and toString");
    for (Entry<String, String> entry : map.entrySet()) {
        System.out.println(entry);
    }
    System.out.println();

    System.out.println("using entrySet and manual string creation");
    for (Entry<String, String> entry : map.entrySet()) {
        System.out.println(entry.getKey() + "=" + entry.getValue());
    }
    System.out.println();

    System.out.println("using keySet");
    for (String key : map.keySet()) {
        System.out.println(key + "=" + map.get(key));
    }
    System.out.println();

Output

using entrySet and toString
key1=value1
key2=value2
key3=value3

using entrySet and manual string creation
key1=value1
key2=value2
key3=value3

using keySet
key1=value1
key2=value2
key3=value3
Answer from Stefan Dollase on Stack Overflow
๐ŸŒ
W3Schools
w3schools.com โ€บ java โ€บ java_hashmap.asp
Java HashMap
Keys and values in a HashMap are actually objects. In the examples above, we used objects of type "String". Remember that a String in Java is an object (not a primitive type). To use other types, such as int, you must specify an equivalent wrapper class: Integer.
๐ŸŒ
Vogella
vogella.com โ€บ tutorials โ€บ JavaMap โ€บ article.html
Using the map datastructures like HashMap and ConcurrentHashMap in Java
September 25, 2024 - This factory method supports a maximum of 10 key-value pairs and requires at least Java 9. Map<String, String> map = Map.of("key1", "value1", "key2", "value2"); Map<String, String> map = Map.ofEntries( Map.entry("key1", "value1"), Map.entry("key2", "value2"), // more entries can be added here ...
Discussions

dictionary - Map to String in Java - Stack Overflow
When I do System.out.println(map) in Java, I get a nice output in stdout. How can I obtain this same string representation of a Map in a variable without meddling with standard output? Something like More on stackoverflow.com
๐ŸŒ stackoverflow.com
How to define schema for Map<String, String>
I have a simple java dictionary as HashMap . The sample json will look like the below format. The no of elements in the dict may be zero or more. The values are not complex obj... More on github.com
๐ŸŒ github.com
2
1
java - How to achieve this Map<String, List<>> structure - Stack Overflow
I have data like below: Key value ----- ------ car toyota car bmw car honda fruit apple fruit banana computer acer computer asus computer ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
hash map - How to make Map<String, Object> expendible(?) safely in java - Code Review Stack Exchange
I am building some statistic counter in Java. Each metric could have flexible depth so the implementation should able to explore the depth when value insert or update. So I implement like below (with More on codereview.stackexchange.com
๐ŸŒ codereview.stackexchange.com
December 18, 2020
๐ŸŒ
Java Code Geeks
examples.javacodegeeks.com โ€บ home โ€บ java development โ€บ core java
Converting String or String Array to Map in Java - Java Code Geeks
October 10, 2023 - We can also use the String.split() method to convert a string to a map in Java. This involves splitting the input string into tokens based on a delimiter, and then parsing these tokens into key-value pairs. Here is an example:
๐ŸŒ
Baeldung
baeldung.com โ€บ home โ€บ java โ€บ java collections โ€บ java map โ€บ converting map to map in java
Converting Map to Map in Java | Baeldung
January 8, 2024 - We can build a checkAndTransform() method to perform a safe conversion: Map<String, String> checkAndTransform(Map<String, Object> inputMap) { Map<String, String> result = new HashMap<>(); for (Map.Entry<String, Object> entry : inputMap.entrySet()) ...
๐ŸŒ
Baeldung
baeldung.com โ€บ home โ€บ java โ€บ java collections โ€บ java map โ€บ map to string conversion in java
Map to String Conversion in Java | Baeldung
January 8, 2026 - In this tutorial, weโ€™ll focus on conversion from a Map to a String and the other way around. First, weโ€™ll see how to achieve these using core Java methods, and afterward, weโ€™ll use some third-party libraries. In all examples, weโ€™re going to use the same Map implementation:
Find elsewhere
๐ŸŒ
W3Schools
w3schools.com โ€บ java โ€บ java_ref_string.asp
Java String Reference
The String class has a set of built-in methods that you can use on strings.
๐ŸŒ
W3Schools
w3schools.com โ€บ JAVA โ€บ java_map.asp
Java Map
The Map interface is a part of the Java Collections Framework and is used to store key-value pairs.
๐ŸŒ
GeeksforGeeks
geeksforgeeks.org โ€บ java โ€บ convert-string-or-string-array-to-hashmap-in-java
Convert String or String Array to HashMap In Java - GeeksforGeeks
July 23, 2025 - // Convert String or String array ... contains the student name and // roll number separated by a comma String student = "Aashish:1, Bina:2, Chintu:3"; // New HashMap obj Map<String, String> hashMap = new HashMap<String, String>(); ...
๐ŸŒ
Baeldung
baeldung.com โ€บ home โ€บ java โ€บ java string โ€บ converting string or string array to map in java
Converting String or String Array to Map in Java | Baeldung
January 8, 2024 - In this article, we have explored different techniques for converting Strings and String arrays into Maps in Java. We covered basic conversions, handling duplicate keys, and provided JUnit test examples to ensure the correctness of the implementations.
๐ŸŒ
javaspring
javaspring.net โ€บ blog โ€บ java-map-string-string
Mastering Java `Map<String, String>` โ€” javaspring.net
There are several ways to iterate ... public static void main(String[] args) { Map<String, String> map = new HashMap<>(); map.put("animal", "dog"); map.put("bird", "eagle"); for (Map.Entry<String, String> entry : map.entrySet()) ...
๐ŸŒ
W3Schools
w3schools.com โ€บ java โ€บ java_strings.asp
Java Strings
A String in Java is actually an object, which means it contains methods that can perform certain operations on strings. For example, you can find the length of a string with the length() method:
๐ŸŒ
javathinking
javathinking.com โ€บ blog โ€บ convert-map-to-string-java
Convert Map to String in Java โ€” javathinking.com
Whether you are logging, serializing, or passing data, being able to convert a Map to a string effectively is an important skill in Java programming. Yes, you can. However, you need to ensure that the toString method of the custom objects is properly implemented to provide a meaningful string representation. Using StringBuilder is generally the fastest for large Maps because it avoids the overhead of creating multiple intermediate strings. You may need to escape special characters before converting the Map to a string. For example, if you are creating a JSON - like string, you need to escape characters like quotes and backslashes.
Top answer
1 of 2
1
if(curr > keys.length -1) return;

// ... 

Object currVal = target.get(currKey);

Don't shorten variable names just because you can. It makes the code harder to read and harder to maintain.


if(curr > keys.length -1) return;

Ideally you're always use a "full block if" for statements, as it makes it easier to see the return condition.

if (curr > keys.length - 1) {
    return;
}

Also, your formatting seems to be off in a few cases. I'd suggest to always use an automatic formatter in your workflow so that you never need to worry about the formatting of your code ever again.


target.put(currKey, 1);

int and Integer are requiring conversion between each other, that's called "boxing" and might or might not be a problem when doing it implicit.


You ain't got any sort of error handling, if I see this right.


So, let's get to the design. If I understand your example right, what you want to do is to have a list of keys, which represent the path through the tree structure, and the last key keeps a counter how often it was added.

There are several ways to achieve that, and the question is what exactly you're trying to achieve to know which one applies.

For every further implementation, we'll assume the following interface:

public class CounterTreeMap {
    public int getValue(String... keys);
    public void putValue(String... keys);
}

You notice that we've already simplified how to use this class, the rest are implementation details. Additionally, I'll skip tests on the input parameters for brevity.

The cheap one

The cheapest solution is to "fake" depth by hashing the input array and having a single Map. However, this implementation would be able to store a value for the path "A, B" as well as for "A, B, C".

protected Map<Integer, Integer> values = new HashMap<>();

public int getValue(String... keys) {
    int keysHash = Arrays.hashCode(keys);
    // TODO We are missing logic here to handle possible hash collisions.
    Integer value = values.get(Integer.valueOf(keysHash));
    
    if (value != null) {
        return value.intValue();
    } else {
        return -1; // Or throw exception here.
    }
}

The nearly as cheap one

Given the problems from the previous implementation, we can fix at least the possible has collisions by using the full path as key instead.

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

public int getValue(String... keys) {
    String key = String.join("/", keys);
    Integer value = values.get(Integer.valueOf(key));
    
    if (value != null) {
        return value.intValue();
    } else {
        return -1; // Or throw exception here.
    }
}

Now that solves the possible hash collisions, but still allows us to store values at each node, basically. That might even be wanted, I don't know, you never specified.

The one with the many maps

As you've done, we can build a hierarchy of Maps, however, I'd base decisions on the type of the encountered leaf.

Recursive implementation:

protected Map<String, Object> tree = new HashMap<>();

public int getValue(String... keys) {
    return getValue(tree, keys, 0);
}

protected int getValue(Map<String, Object> branch, String[] keys, int currentKeyIndex) {
    String key = keys[currentKeyIndex];
    Object value = branch.get(key);
    
    if (value == null) {
        return -1; // Or throw an exception here.
    } else if (value instanceof Integer) {
        return ((Integer)value).intValue();
    } else if (value instanceof Map<?, ?>) {
        return getValue((Map<String, Object)value, keys, currentKeyIndex + 1)
    }
}

Implementation using a loop instead:

protected Map<String, Object> tree = new HashMap<>();

public int getValue(String... keys) {
    Map<String, Object> branch = tree;
    int currentKeyIndex = 0;
    
    while (branch != null) {
        String key = keys[currentKeyIndex];
        Object value = branch.get(key);
        
        if (value == null) {
            return -1; // Or throw an exception here.
        } else if (value instanceof Integer) {
            return ((Integer)value).intValue();
        } else if (value instanceof Map<?, ?>) {
            branch = (Map<String, Object)value;
            currentKeyIndex++;
        }
    }
}

Both implementations lack proper checks, though, but you should get the idea.


Given the use case as example, I'd go with flattening/joining the path into a single string (using "/" as separator) and, when adding a new path, increasing the existing value or setting a new one if it does not exist.

2 of 2
1

It took a long time just to understand what your program is doing and how it might be useful.

You were concerned about the unchecked casting, and that's a valid concern, but maybe it's not a problem if you make that behaviour clear, for instance, by including a failure scenario in the test case or JavaDoc, in which case you are just stating it's the responsibility of the other code that makes use of this to only pass valid arguments.

Here's a version of the same thing that's hopefully a bit better at describing what it does. With things a bit more plain to read, you can decide if you want your program to behave differently in the failure case -- maybe it should throw a checked exception instead of ClassCastException or maybe you want to actually support these cases?

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

/**
 * I am building some statistic counter in Java. Each metric could have flexible
 * depth so the implementation should able to explore the depth when value
 * insert or update.
 *
 */
public class StatisticsCounter {

    private Map<String, Object> rootMap = new HashMap<>();

    public void increment(String... paths) {
        recursiveIncrement(rootMap, paths, 0);
    }

    private void recursiveIncrement(Map<String, Object> targetMap, String[] pathToTarget,
            int pathIndex) {

        final int targetDepth = pathToTarget.length - 1;
        final String counterName = pathToTarget[pathIndex];

        if (pathIndex == targetDepth && targetMap.containsKey(counterName)) {
            // assume objects at the target depth are int
            targetMap.put(counterName, (int) targetMap.get(counterName) + 1);
        } else if (pathIndex == targetDepth) {
            // at the target depth, put ints
            targetMap.put(counterName, 1);
        } else if (targetMap.containsKey(counterName)) {
            // assume object is a map, recur into it for the next counter name
            recursiveIncrement((Map<String, Object>) targetMap.get(counterName),
                    pathToTarget, pathIndex + 1);
        } else {
            // create a new map and recur into it for the next counter name
            Map<String, Object> emptyMap = new HashMap<>();
            targetMap.put(counterName, emptyMap);
            recursiveIncrement(emptyMap, pathToTarget, pathIndex + 1);
        }
    }

    public String toString() {
        return rootMap.toString();
    }

    @Test
    public void test() {
        StatisticsCounter counters = new StatisticsCounter();
        counters.increment("USA", "California", "Los Angeles");
        counters.increment("USA", "California", "Los Angeles");
        counters.increment("USA", "California", "San Diego");
        counters.increment("USA", "California", "Bay Area", "San Francisco",
                "Financial District");
        counters.increment("USA", "Tennessee", "Memphis");
        counters.increment("USA", "Tennessee", "Memphis");
        counters.increment("Italy", "Venice");
        counters.increment("Italy", "Rome");
        counters.increment("Italy", "Venice");
        counters.increment("Vatican City");
        counters.increment("Vatican City");

        try {
            counters.increment("USA", "California");
            fail("Cannot increment a key that already has subcounters");
        } catch (ClassCastException expectedException) {
            assertEquals("java.util.HashMap cannot be cast to java.lang.Integer",
                    expectedException.getMessage());
        }

        try {
            counters.increment("USA", "Tennessee", "Memphis", "Graceland");
            fail("Cannot add subcounters to a key that already has a count");
        } catch (ClassCastException expectedException) {
            assertEquals("java.lang.Integer cannot be cast to java.util.Map",
                    expectedException.getMessage());
        }

        System.out.println(counters);
        // {USA={Tennessee={Memphis=2}, California={San Diego=1, Bay Area={San
        // Francisco={Financial District=1}}, Los Angeles=2}}, Italy={Rome=1, Venice=2},
        // Vatican City=2}
    }
}
๐ŸŒ
Oracle
docs.oracle.com โ€บ javase โ€บ tutorial โ€บ collections โ€บ interfaces โ€บ map.html
The Map Interface (The Javaโ„ข Tutorials > Collections > Interfaces)
See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases. See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases. A Map is an object that maps keys to values.