Skip to content

Commit 0ff1726

Browse files
authored
BAEL-8864: More random Map implementations and examples (#18920)
* BAEL-8864: More random Map implementations and examples * BAEL-8864: Fixed the access modifiers and added tests
1 parent 042c685 commit 0ff1726

File tree

10 files changed

+572
-0
lines changed

10 files changed

+572
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.baeldung.map.randommapkey;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.concurrent.ThreadLocalRandom;
8+
9+
public class OptimizedRandomKeyTrackingMap<K, V> {
10+
11+
private final Map<K, V> delegate = new HashMap<>();
12+
private final List<K> keys = new ArrayList<>();
13+
private final Map<K, Integer> keyToIndex = new HashMap<>();
14+
15+
public void put(K key, V value) {
16+
V previousValue = delegate.put(key, value);
17+
if (previousValue == null) {
18+
keys.add(key);
19+
keyToIndex.put(key, keys.size() - 1);
20+
}
21+
}
22+
23+
public V remove(K key) {
24+
V removedValue = delegate.remove(key);
25+
if (removedValue != null) {
26+
Integer index = keyToIndex.remove(key);
27+
if (index != null) {
28+
removeKeyAtIndex(index);
29+
}
30+
}
31+
return removedValue;
32+
}
33+
34+
private void removeKeyAtIndex(int index) {
35+
int lastIndex = keys.size() - 1;
36+
if (index == lastIndex) {
37+
keys.remove(lastIndex);
38+
return;
39+
}
40+
41+
K lastKey = keys.get(lastIndex);
42+
keys.set(index, lastKey);
43+
keyToIndex.put(lastKey, index);
44+
keys.remove(lastIndex);
45+
}
46+
47+
public V getRandomValue() {
48+
if (keys.isEmpty()) {
49+
return null;
50+
}
51+
52+
int randomIndex = ThreadLocalRandom.current().nextInt(keys.size());
53+
K randomKey = keys.get(randomIndex);
54+
return delegate.get(randomKey);
55+
}
56+
}
57+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.baeldung.map.randommapkey;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
import java.util.Map;
7+
8+
public class RandomKeyShuffleMap {
9+
10+
public <K, V> K getRandomKeyUsingShuffle(Map<K, V> map) {
11+
if (map == null || map.isEmpty()) {
12+
return null;
13+
}
14+
15+
List<K> keys = new ArrayList<>(map.keySet());
16+
Collections.shuffle(keys);
17+
18+
return keys.get(0);
19+
}
20+
}
21+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.baeldung.map.randommapkey;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.concurrent.ThreadLocalRandom;
8+
9+
public class RandomKeyTrackingMap<K, V> {
10+
11+
private final Map<K, V> delegate = new HashMap<>();
12+
private final List<K> keys = new ArrayList<>();
13+
14+
public void put(K key, V value) {
15+
V previousValue = delegate.put(key, value);
16+
if (previousValue == null) {
17+
keys.add(key);
18+
}
19+
}
20+
21+
public V remove(K key) {
22+
V removedValue = delegate.remove(key);
23+
if (removedValue != null) {
24+
int index = keys.indexOf(key);
25+
if (index >= 0) {
26+
keys.remove(index);
27+
}
28+
}
29+
return removedValue;
30+
}
31+
32+
public V getRandomValue() {
33+
if (keys.isEmpty()) {
34+
return null;
35+
}
36+
37+
int randomIndex = ThreadLocalRandom.current().nextInt(keys.size());
38+
K randomKey = keys.get(randomIndex);
39+
return delegate.get(randomKey);
40+
}
41+
42+
}
43+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.baeldung.map.randommapkey;
2+
3+
import java.util.ArrayList;
4+
import java.util.Iterator;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.Map.Entry;
8+
import java.util.concurrent.ThreadLocalRandom;
9+
10+
public class RandomNumberMap {
11+
12+
public <K, V> V getRandomValueUsingList(Map<K, V> map) {
13+
if (map == null || map.isEmpty()) {
14+
return null;
15+
}
16+
17+
List<K> keys = new ArrayList<>(map.keySet());
18+
K randomKey = keys.get(ThreadLocalRandom.current().nextInt(keys.size()));
19+
return map.get(randomKey);
20+
}
21+
22+
public <K, V> V getRandomValueUsingOffset(Map<K, V> map) {
23+
if (map == null || map.isEmpty()) {
24+
return null;
25+
}
26+
27+
int randomOffset = ThreadLocalRandom.current().nextInt(map.size());
28+
Iterator<Entry<K, V>> iterator = map.entrySet().iterator();
29+
for (int i = 0; i < randomOffset; i++) {
30+
iterator.next();
31+
}
32+
33+
return iterator.next().getValue();
34+
}
35+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package com.baeldung.map.randommapkey;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import java.util.concurrent.ThreadLocalRandom;
6+
7+
public class RandomizedMap<K, V> extends HashMap<K, V> {
8+
9+
private final Map<Integer, K> numberToKey = new HashMap<>();
10+
private final Map<K, Integer> keyToNumber = new HashMap<>();
11+
@Override
12+
public V put(K key, V value) {
13+
V oldValue = super.put(key, value);
14+
15+
if (oldValue == null) {
16+
int number = this.size() - 1;
17+
numberToKey.put(number, key);
18+
keyToNumber.put(key, number);
19+
}
20+
21+
return oldValue;
22+
}
23+
24+
@Override
25+
public void putAll(Map<? extends K, ? extends V> m) {
26+
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
27+
put(entry.getKey(), entry.getValue());
28+
}
29+
}
30+
31+
@Override
32+
public V remove(Object key) {
33+
V removedValue = super.remove(key);
34+
35+
if (removedValue != null) {
36+
removeFromTrackingMaps(key);
37+
}
38+
39+
return removedValue;
40+
}
41+
42+
@Override
43+
public boolean remove(Object key, Object value) {
44+
boolean removed = super.remove(key, value);
45+
46+
if (removed) {
47+
removeFromTrackingMaps(key);
48+
}
49+
50+
return removed;
51+
}
52+
53+
@Override
54+
public void clear() {
55+
super.clear();
56+
numberToKey.clear();
57+
keyToNumber.clear();
58+
}
59+
60+
public V getRandomValue() {
61+
if (this.isEmpty()) {
62+
return null;
63+
}
64+
65+
int randomNumber = ThreadLocalRandom.current().nextInt(this.size());
66+
K randomKey = numberToKey.get(randomNumber);
67+
return this.get(randomKey);
68+
}
69+
70+
private void removeFromTrackingMaps(Object key) {
71+
@SuppressWarnings("unchecked")
72+
K keyToRemove = (K) key;
73+
74+
Integer numberToRemove = keyToNumber.get(keyToRemove);
75+
if (numberToRemove == null) {
76+
return;
77+
}
78+
79+
int mapSize = this.size();
80+
int lastIndex = mapSize;
81+
82+
if (numberToRemove == lastIndex) {
83+
numberToKey.remove(numberToRemove);
84+
keyToNumber.remove(keyToRemove);
85+
} else {
86+
K lastKey = numberToKey.get(lastIndex);
87+
if (lastKey == null) {
88+
numberToKey.remove(numberToRemove);
89+
keyToNumber.remove(keyToRemove);
90+
return;
91+
}
92+
93+
numberToKey.put(numberToRemove, lastKey);
94+
keyToNumber.put(lastKey, numberToRemove);
95+
96+
numberToKey.remove(lastIndex);
97+
98+
keyToNumber.remove(keyToRemove);
99+
}
100+
}
101+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.baeldung.map.randommapkey;
2+
3+
import java.util.Arrays;
4+
5+
import static org.junit.Assert.assertNotNull;
6+
import static org.junit.Assert.assertNull;
7+
import static org.junit.Assert.assertTrue;
8+
import org.junit.Test;
9+
10+
public class OptimizedRandomKeyTrackingMapUnitTest {
11+
12+
@Test
13+
public void whenGettingRandomValue_thenValueExistsInMap() {
14+
OptimizedRandomKeyTrackingMap<String, Integer> map = new OptimizedRandomKeyTrackingMap<>();
15+
map.put("apple", 1);
16+
map.put("banana", 2);
17+
map.put("cherry", 3);
18+
19+
Integer randomValue = map.getRandomValue();
20+
21+
assertNotNull(randomValue);
22+
assertTrue(Arrays.asList(1, 2, 3).contains(randomValue));
23+
}
24+
25+
@Test
26+
public void whenRemovingValue_thenRandomValueDoesNotContainRemovedEntry() {
27+
OptimizedRandomKeyTrackingMap<String, Integer> map = new OptimizedRandomKeyTrackingMap<>();
28+
map.put("apple", 1);
29+
map.put("banana", 2);
30+
map.put("cherry", 3);
31+
32+
map.remove("banana");
33+
34+
Integer randomValue = map.getRandomValue();
35+
36+
assertNotNull(randomValue);
37+
assertTrue(Arrays.asList(1, 3).contains(randomValue));
38+
}
39+
40+
@Test
41+
public void whenRemovingAllEntries_thenRandomValueReturnsNull() {
42+
OptimizedRandomKeyTrackingMap<String, Integer> map = new OptimizedRandomKeyTrackingMap<>();
43+
map.put("apple", 1);
44+
45+
map.remove("apple");
46+
47+
Integer valueAfterRemoval = map.getRandomValue();
48+
assertNull(valueAfterRemoval);
49+
}
50+
}
51+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.baeldung.map.randommapkey;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import static org.junit.Assert.assertNotNull;
7+
import static org.junit.Assert.assertNull;
8+
import static org.junit.Assert.assertTrue;
9+
import org.junit.Test;
10+
11+
public class RandomKeyShuffleMapUnitTest {
12+
13+
private final RandomKeyShuffleMap randomKeyShuffleMap = new RandomKeyShuffleMap();
14+
15+
@Test
16+
public void whenGettingRandomKeyUsingShuffle_thenKeyExistsInMap() {
17+
Map<String, Integer> map = new HashMap<>();
18+
map.put("apple", 1);
19+
map.put("banana", 2);
20+
map.put("cherry", 3);
21+
map.put("date", 4);
22+
23+
String randomKey = randomKeyShuffleMap.getRandomKeyUsingShuffle(map);
24+
25+
assertNotNull(randomKey);
26+
assertTrue(map.containsKey(randomKey));
27+
}
28+
29+
@Test
30+
public void whenMapIsEmpty_thenReturningNull() {
31+
Map<String, Integer> map = new HashMap<>();
32+
33+
String randomKey = randomKeyShuffleMap.getRandomKeyUsingShuffle(map);
34+
35+
assertNull(randomKey);
36+
}
37+
38+
@Test
39+
public void whenMapIsNull_thenReturningNull() {
40+
String randomKey = randomKeyShuffleMap.getRandomKeyUsingShuffle(null);
41+
42+
assertNull(randomKey);
43+
}
44+
}
45+

0 commit comments

Comments
 (0)