javaintermediate
MultiMap and BiMap Implementations
Implement MultiMap and BiMap data structures for one-to-many and bidirectional key-value mappings.
javaPress ⌘/Ctrl + Shift + C to copy
import java.util.*;
import java.util.stream.Collectors;
// MultiMap: one key → many values
public class MultiMap<K, V> {
private final Map<K, List<V>> map = new LinkedHashMap<>();
public void put(K key, V value) {
map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
}
public List<V> get(K key) {
return map.getOrDefault(key, List.of());
}
public boolean remove(K key, V value) {
List<V> values = map.get(key);
if (values == null) return false;
boolean removed = values.remove(value);
if (values.isEmpty()) map.remove(key);
return removed;
}
public Set<K> keys() { return map.keySet(); }
public int size() { return map.values().stream().mapToInt(List::size).sum(); }
@Override
public String toString() { return map.toString(); }
}
// BiMap: bidirectional map (key ↔ value both unique)
class BiMap<K, V> {
private final Map<K, V> forward = new LinkedHashMap<>();
private final Map<V, K> inverse = new LinkedHashMap<>();
public V put(K key, V value) {
// Remove existing mappings
if (forward.containsKey(key)) inverse.remove(forward.get(key));
if (inverse.containsKey(value)) forward.remove(inverse.get(value));
forward.put(key, value);
inverse.put(value, key);
return value;
}
public V get(K key) { return forward.get(key); }
public K getKey(V value) { return inverse.get(value); }
public V removeByKey(K key) {
V value = forward.remove(key);
if (value != null) inverse.remove(value);
return value;
}
public K removeByValue(V value) {
K key = inverse.remove(value);
if (key != null) forward.remove(key);
return key;
}
public boolean containsKey(K key) { return forward.containsKey(key); }
public boolean containsValue(V value) { return inverse.containsKey(value); }
public int size() { return forward.size(); }
public BiMap<V, K> inverse() {
BiMap<V, K> inv = new BiMap<>();
inv.forward.putAll(this.inverse);
inv.inverse.putAll(this.forward);
return inv;
}
@Override
public String toString() { return forward.toString(); }
}
class Demo {
public static void main(String[] args) {
// MultiMap
MultiMap<String, String> tags = new MultiMap<>();
tags.put("java", "spring-boot");
tags.put("java", "concurrency");
tags.put("java", "streams");
tags.put("python", "django");
System.out.println("java tags: " + tags.get("java")); // [spring-boot, concurrency, streams]
System.out.println("Total: " + tags.size()); // 4
// BiMap
BiMap<String, Integer> codes = new BiMap<>();
codes.put("US", 1);
codes.put("UK", 44);
codes.put("IN", 91);
System.out.println("US code: " + codes.get("US")); // 1
System.out.println("Code 44: " + codes.getKey(44)); // UK
BiMap<Integer, String> reversed = codes.inverse();
System.out.println("Reversed: " + reversed.get(91)); // IN
}
}Use Cases
- Tag/category systems with multiple values per key
- Bidirectional lookups (e.g., country codes)
- Index inversion and reverse mapping
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
javabeginner
HashMap Operations and Patterns
Essential HashMap operations: put, get, merge, compute, getOrDefault, and iteration patterns.
Best for: Counting word frequencies in text
#java#collections
javabeginner
Java List Operations and Utilities
Essential List operations: create, sort, search, transform, partition, and immutable list patterns.
Best for: Common list manipulation patterns
#java#collections
javaintermediate
Java Map — Advanced Operations
Master Map operations: compute, merge, getOrDefault, multi-map, BiMap, and stream-based grouping.
Best for: Aggregation and grouping operations on data
#java#map
javabeginner
Java Streams — Filter, Map, Collect
Process collections with Java Streams: filter, map, flatMap, reduce, and collect to lists or maps.
Best for: Transforming and filtering collections
#java#streams