Simple answer
You're doing it wrong. This stuff already exists; use e.g. guava's ImmutableMap.builder
, or use the existing Map.of()
. It's not quite the same: Your stack of method 'hardcodes' that it's a Map<String, Object>
, but presumably it's not worth rewriting the ImmutableMap or Map.of API for just that benefit.
Complicated answers
Note that your naming is off. addToMap
is not a good method name, particularly not for a method that creates an entirely new map. addToMap
just screams 'this adds the provided arguments to an existing map', which these methods don't do. So I took the liberty of fixing your names for you. These methods also have no state so should be static.
Option #1: Passthrough to a private method.
public static Map<String, Object> newMap(String name, Object value) {
return newMap0(name, value);
}
public static Map<String, Object> newMap(String name1, Object value2, String name1, Object value2) {
return newMap0(name1, value1, name2, value2);
}
public static Map<String, Object> newMap(String name1, Object value1, String name2, Object value2, String name3, Object value3) {
return newMap0(name1, value1, name2, value2, name3, value3);
}
// and so on
private static Map<String, Object> newMap0(Object... v) {
assert v.length % 2 == 0;
var out = new HashMap<String, Object>();
for (int i = 0; i < v.length; i +=2) out.put((String) v[i], v[i + 1]);
return out;
}
That last method is type-wise a disaster (freely allows passing in an odd number of arguments or non-string keys), which is why it has no business being a part of your API. That's why it's private.
option 2: a builder
Check out ImmutableMap.builder for the full treatise. This is a whole bunch of complicated code, but it makes for a decently nice API. Your goal is that a caller can do this:
YourUtilityClassThing.newMap()
.put("a", 5)
.put("b", 10)
.build();
This has the considerable advantage of being 'typesafe' (you can only pass in String keys and any object for a value), as well as allowing an infinite amount of k/v pairs. The downside is, your API needs quite a bit of code to make this work.
I cannot stress strongly enough how you should not be reinventing this wheel and using e.g. guava's ImmutableMap.Builder instead, if you like this approach.
option 3: a tuple type
Make an explicit tuple type and write your API docs to indicate that it is not usable without aggressive use of static imports, but with static imports, it's nice. The callers end up writing:
import static com.foo.YourThingie.*;
....
newMap(p("a", 5), p("b", 5));
You need quite a bit of code in YourThingie
to tie this together, but it's typesafe and supports infinite arguments:
class YourThingie {
public static final class KeyValuePair {
String key; Object value;
}
public static KeyValuePair p(String key, Object value) {
KeyValuePair kvp = new KeyValuePair();
kvp.key = key; kvp.value = value;
return kvp;
}
public static Map<String, Object> newMap(KeyValuePair... pairs) {
var out = new HashMap<String, Object>();
for (var pair : pairs) out.put(pair.key, pair.value);
return out;
}
}
You may want to make KeyValuePair a proper class: Immutable, with a 2-arg constructor, and a proper toString/equals/hashCode impl. That either involves a pageful of boilerplate, or use JDK16's record, or use Project Lombok's @Value
to get this (DISCLAIMER: I'm known to contribute to lombok quite a bit).