Skip to content

Commit d7a8201

Browse files
paulk-asertgarydgregory
authored andcommitted
provide an inverse method for multimaps
1 parent a60ee0c commit d7a8201

12 files changed

+141
-0
lines changed

src/main/java/org/apache/commons/collections4/MultiMapUtils.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Collection;
2121
import java.util.HashSet;
2222
import java.util.List;
23+
import java.util.Map;
2324
import java.util.Set;
2425

2526
import org.apache.commons.collections4.bag.HashBag;
@@ -170,6 +171,27 @@ public static boolean isEmpty(final MultiValuedMap<?, ?> map) {
170171
return map == null || map.isEmpty();
171172
}
172173

174+
/**
175+
* A utility method to invert the mappings from an input MultiValuedMap
176+
* and add them to an output MultiValuedMap. Use this method to have complete
177+
* control of the output MultiValuedMap or when merging several inverse mappings.
178+
* In simple cases, consider just using the {@link MultiValuedMap#inverted()} method.
179+
*
180+
* @param input take key-to-value mappings from here
181+
* @param output add value-to-key mappings here
182+
* @param <K> the output MultiValuedMap key type
183+
* @param <V> the output MultiValuedMap value type
184+
* @param <M> the output MultiValuedMap with key and value types reversed compared with input
185+
* @return the updated output MultiValuedMap
186+
*/
187+
public static <K, V, M extends MultiValuedMap<K, V>>
188+
M invert(MultiValuedMap<? extends V, ? extends K> input, M output) {
189+
for (Map.Entry<? extends V, ? extends K> e : input.entries()) {
190+
output.put(e.getValue(), e.getKey());
191+
}
192+
return output;
193+
}
194+
173195
/**
174196
* Creates a {@link ListValuedMap} with an {@link java.util.ArrayList ArrayList} as
175197
* collection class to store the values mapped to a key.

src/main/java/org/apache/commons/collections4/MultiValuedMap.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,17 @@ public interface MultiValuedMap<K, V> {
140140
*/
141141
Collection<V> get(K key);
142142

143+
/**
144+
* Returns a new MultiValuedMap with inverted mappings.
145+
* The new multimap will have a value-to-key mapping
146+
* for each key-to-value mapping in the original.
147+
*
148+
* @return a new MultiValuedMap with inverted mappings
149+
*/
150+
default MultiValuedMap<V, K> inverted() {
151+
throw new UnsupportedOperationException();
152+
}
153+
143154
/**
144155
* Returns {@code true} if this map contains no key-value mappings.
145156
*

src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMap.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.HashMap;
2626
import java.util.Map;
2727

28+
import org.apache.commons.collections4.MultiMapUtils;
2829
import org.apache.commons.collections4.MultiValuedMap;
2930

3031
/**
@@ -118,6 +119,11 @@ protected ArrayList<V> createCollection() {
118119
return new ArrayList<>(initialListCapacity);
119120
}
120121

122+
@Override
123+
public ArrayListValuedHashMap<V, K> inverted() {
124+
return MultiMapUtils.invert(this, new ArrayListValuedHashMap<V, K>());
125+
}
126+
121127
/**
122128
* Deserializes an instance from an ObjectInputStream.
123129
*

src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMap.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.LinkedHashMap;
2626
import java.util.Map;
2727

28+
import org.apache.commons.collections4.MultiMapUtils;
2829
import org.apache.commons.collections4.MultiValuedMap;
2930

3031
/**
@@ -118,6 +119,11 @@ protected ArrayList<V> createCollection() {
118119
return new ArrayList<>(initialListCapacity);
119120
}
120121

122+
@Override
123+
public ArrayListValuedLinkedHashMap<V, K> inverted() {
124+
return MultiMapUtils.invert(this, new ArrayListValuedLinkedHashMap<>());
125+
}
126+
121127
/**
122128
* Deserializes an instance from an ObjectInputStream.
123129
*

src/main/java/org/apache/commons/collections4/multimap/HashSetValuedHashMap.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.HashSet;
2525
import java.util.Map;
2626

27+
import org.apache.commons.collections4.MultiMapUtils;
2728
import org.apache.commons.collections4.MultiValuedMap;
2829

2930
/**
@@ -117,6 +118,11 @@ protected HashSet<V> createCollection() {
117118
return new HashSet<>(initialSetCapacity);
118119
}
119120

121+
@Override
122+
public HashSetValuedHashMap<V, K> inverted() {
123+
return MultiMapUtils.invert(this, new HashSetValuedHashMap<V, K>());
124+
}
125+
120126
/**
121127
* Deserializes an instance from an ObjectInputStream.
122128
*

src/main/java/org/apache/commons/collections4/multimap/LinkedHashSetValuedLinkedHashMap.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.LinkedHashSet;
2525
import java.util.Map;
2626

27+
import org.apache.commons.collections4.MultiMapUtils;
2728
import org.apache.commons.collections4.MultiValuedMap;
2829

2930
/**
@@ -117,6 +118,11 @@ protected LinkedHashSet<V> createCollection() {
117118
return new LinkedHashSet<>(initialSetCapacity);
118119
}
119120

121+
@Override
122+
public LinkedHashSetValuedLinkedHashMap<V, K> inverted() {
123+
return MultiMapUtils.invert(this, new LinkedHashSetValuedLinkedHashMap<V, K>());
124+
}
125+
120126
/**
121127
* Deserializes an instance from an ObjectInputStream.
122128
*

src/test/java/org/apache/commons/collections4/MultiMapUtilsTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import java.util.Set;
3030

3131
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
32+
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
33+
import org.apache.commons.collections4.multimap.LinkedHashSetValuedLinkedHashMap;
3234
import org.junit.jupiter.api.Test;
3335

3436
/**
@@ -116,6 +118,30 @@ void testGetValuesAsSet() {
116118
assertEquals(new HashSet<>(Arrays.asList(values)), set);
117119
}
118120

121+
@Test
122+
void testInvert() {
123+
final HashSetValuedHashMap<String, String> usages = new HashSetValuedHashMap<>();
124+
125+
final LinkedHashSetValuedLinkedHashMap<String, String> deps = new LinkedHashSetValuedLinkedHashMap<>();
126+
deps.put("commons-configuration2", "commons-logging");
127+
deps.put("commons-configuration2", "commons-lang3");
128+
deps.put("commons-configuration2", "commons-text");
129+
deps.put("commons-beanutils", "commons-collections");
130+
deps.put("commons-beanutils", "commons-logging");
131+
MultiMapUtils.invert(deps, usages);
132+
final Set<String> loggingUsagesCompile = usages.get("commons-logging");
133+
assertEquals("[commons-configuration2, commons-beanutils]", loggingUsagesCompile.toString());
134+
final Set<String> codecUsagesCompile = usages.get("commons-codec");
135+
assertEquals("[]", codecUsagesCompile.toString());
136+
137+
final LinkedHashSetValuedLinkedHashMap<String, String> optionalDeps = new LinkedHashSetValuedLinkedHashMap<>();
138+
optionalDeps.put("commons-configuration2", "commons-codec");
139+
optionalDeps.put("commons-collections", "commons-codec");
140+
MultiMapUtils.invert(optionalDeps, usages);
141+
final Set<String> codecUsagesAll = usages.get("commons-codec");
142+
assertEquals("[commons-collections, commons-configuration2]", codecUsagesAll.toString());
143+
}
144+
119145
@Test
120146
void testIsEmptyWithEmptyMap() {
121147
assertTrue(MultiMapUtils.isEmpty(new ArrayListValuedHashMap<>()));

src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,19 @@ void testEqualsHashCodeContract() {
9393
assertNotSame(map1.hashCode(), map2.hashCode());
9494
}
9595

96+
@Test
97+
void testInverted() {
98+
final ArrayListValuedHashMap<String, String> shopping = new ArrayListValuedHashMap<>(4);
99+
shopping.put("Alice", "Bread");
100+
shopping.put("Alice", "Milk");
101+
shopping.put("Alice", "Milk");
102+
shopping.put("Bob", "Pizza");
103+
shopping.put("Bob", "Bread");
104+
shopping.put("Bob", "Bread");
105+
assertEquals("{Pizza=[Bob], Bread=[Bob, Bob, Alice], Milk=[Alice, Alice]}",
106+
shopping.inverted().toString());
107+
}
108+
96109
@Test
97110
@SuppressWarnings("unchecked")
98111
void testListValuedMapAdd() {

src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMapTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,19 @@ void testEqualsHashCodeContract() {
9999
assertNotSame(map1.hashCode(), map2.hashCode());
100100
}
101101

102+
@Test
103+
void testInverted() {
104+
final ArrayListValuedLinkedHashMap<String, String> shopping = new ArrayListValuedLinkedHashMap<>(4);
105+
shopping.put("Alice", "Bread");
106+
shopping.put("Alice", "Milk");
107+
shopping.put("Alice", "Milk");
108+
shopping.put("Bob", "Pizza");
109+
shopping.put("Bob", "Bread");
110+
shopping.put("Bob", "Bread");
111+
assertEquals("{Bread=[Alice, Bob, Bob], Milk=[Alice, Alice], Pizza=[Bob]}",
112+
shopping.inverted().toString());
113+
}
114+
102115
@Test
103116
@SuppressWarnings("unchecked")
104117
void testListValuedMapAdd() {

src/test/java/org/apache/commons/collections4/multimap/HashSetValuedHashMapTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,19 @@ void testHashSetValueHashMap_1() {
110110
assertEquals("{}", map3.toString());
111111
}
112112

113+
@Test
114+
void testInverted() {
115+
final HashSetValuedHashMap<String, String> dependencies = new HashSetValuedHashMap<>();
116+
dependencies.put("commons-configuration2", "commons-logging");
117+
dependencies.put("commons-configuration2", "commons-lang3");
118+
dependencies.put("commons-configuration2", "commons-text");
119+
dependencies.put("commons-beanutils", "commons-collections");
120+
dependencies.put("commons-beanutils", "commons-logging");
121+
final Set<String> loggingUsages = dependencies.inverted().get("commons-logging");
122+
assertEquals("[commons-beanutils, commons-configuration2]",
123+
loggingUsages.toString());
124+
}
125+
113126
@Test
114127
@SuppressWarnings("unchecked")
115128
void testSetValuedMapAdd() {

0 commit comments

Comments
 (0)