- Your idea of a common empty interface (
FooBarInterface
) is fine and solves the problem.
- If you want to accommodate more enums and possibly ones that don’t implement your interface, you may also use
Enum
as map key type: Map<Enum<?>, Integer> lookupTable = Map.of(Foo.FOO1, 1, Foo.FOO2, 3, Foo.FOO3, 5, Bar.BAR1, 6, Bar.BAR2, 8, Bar.BAR3, 11);
.
In both cases, depending on circumstances I would probably still keep two separate maps and only make a union of them for iteration when needed.
To carry this idea a little further I might even consider developing a view onto the union of the two maps’ entry sets. You would need to develop your own Iterator
implementation that first iterates the entry set of the first map, then the second map, then finally signals the end of the iteration (hasNext()
returns false
).
Type safe? What you say you want has an inherent “type unsafety” to it. Live with it. It’s only a small cost on top of the basic type problem with the java.util.Map
interface: its get
method accepts any object, not just an object of the declared key type (this is for historical reasons).
PS In the Java versions I have used int
cannot be a value type of a Map
or a type parameter for a generic type at all. It may have been added later. If not, you will have to use Integer
.
Edit: the terse way of iterating two maps
One way to iterate two (or more) maps is through a stream operation:
Map<Foo, Integer> fooLookupTable = new EnumMap<>(Map.of(Foo.FOO1, 2, Foo.FOO2, 3, Foo.FOO3, 8));
Map<Bar, Integer> barLookupTable = new EnumMap<>(Map.of(Bar.BAR1, 4, Bar.BAR2, 6, Bar.BAR3, 11));
Stream.of(fooLookupTable, barLookupTable)
.flatMap(m -> m.entrySet().stream())
.forEach(e -> System.out.format("%4s (%3s) %2d%n",
e.getKey(), e.getKey().getClass().getSimpleName(), e.getValue()));
Output:
FOO1 (Foo) 2
FOO2 (Foo) 3
FOO3 (Foo) 8
BAR1 (Bar) 4
BAR2 (Bar) 6
BAR3 (Bar) 11
Edit: code for an iterator iterating multiple collections
For keeping two separate lookup maps and iterate over both at once, you may want to have code like the following:
Map<Foo, Integer> fooLookupTable = new EnumMap<>(Map.of(Foo.FOO1, 2, Foo.FOO2, 3, Foo.FOO3, 8));
Map<Bar, Integer> barLookupTable = new EnumMap<>(Map.of(Bar.BAR1, 4, Bar.BAR2, 6, Bar.BAR3, 11));
IterableUnion<Entry<? extends Enum<?>, Integer>> union
= new IterableUnion<>(fooLookupTable.entrySet(), barLookupTable.entrySet());
for (Map.Entry<? extends Enum<?>, Integer> e : union) {
System.out.format("%4s (%3s) %2d%n",
e.getKey(), e.getKey().getClass().getSimpleName(), e.getValue());
}
Output is:
FOO1 (Foo) 2
FOO2 (Foo) 3
FOO3 (Foo) 8
BAR1 (Bar) 4
BAR2 (Bar) 6
BAR3 (Bar) 11
I am using an IterableUnion
class like the following. I think it has got some generality to it: you can iterate more than two collections or other things implementing Iterable
, and the element type may vary.
public class IterableUnion<E> implements Iterable<E> {
private class UnionIterator implements Iterator<E> {
private int index = 0;
private Iterator<? extends E> iteratorForIndex;
UnionIterator() {
if (iterables.length > 0) {
iteratorForIndex = iterables[0].iterator();
}
}
@Override
public boolean hasNext() {
if (index == iterables.length) {
return false;
}
while (true) {
if (iteratorForIndex.hasNext()) {
return true;
}
index++;
if (index == iterables.length) {
return false;
}
iteratorForIndex = iterables[index].iterator();
}
}
@Override
public E next() {
if (! hasNext()) {
throw new NoSuchElementException();
}
return iteratorForIndex.next();
}
}
private final Iterable<? extends E>[] iterables;
@SafeVarargs
public IterableUnion(Iterable<? extends E>... iterables) {
this.iterables = iterables;
}
@Override
public Iterator<E> iterator() {
return new UnionIterator();
}
}