Skip to content

Commit a00a331

Browse files
author
burdo
committed
introduce HashKeyMap
this ensures a map of CharSequence allows different implementations using hasCode()
1 parent 9a01158 commit a00a331

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package org.comroid.api.map;
2+
3+
import lombok.AccessLevel;
4+
import lombok.NoArgsConstructor;
5+
import lombok.Value;
6+
import lombok.experimental.FieldDefaults;
7+
import lombok.experimental.NonFinal;
8+
import org.comroid.api.Polyfill;
9+
import org.jspecify.annotations.NonNull;
10+
import org.jspecify.annotations.Nullable;
11+
12+
import java.util.Collection;
13+
import java.util.HashSet;
14+
import java.util.Map;
15+
import java.util.Optional;
16+
import java.util.Set;
17+
import java.util.stream.Collectors;
18+
19+
@NoArgsConstructor
20+
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
21+
public class HashKeyMap<K, V> implements Map<K, V> {
22+
Set<Entry> entries = new HashSet<>();
23+
24+
public HashKeyMap(Map<? extends K, ? extends V> map) {
25+
putAll(map);
26+
}
27+
28+
@Override
29+
public int size() {
30+
return entries.size();
31+
}
32+
33+
@Override
34+
public boolean isEmpty() {
35+
return entries.isEmpty();
36+
}
37+
38+
@Override
39+
public boolean containsKey(Object key) {
40+
return entry(key.hashCode()).isPresent();
41+
}
42+
43+
@Override
44+
public boolean containsValue(Object value) {
45+
return value != null && entries.stream().map(Entry::getValue).anyMatch(value::equals);
46+
}
47+
48+
@Override
49+
public V get(Object key) {
50+
return entry(key.hashCode()).map(Entry::getValue).orElse(null);
51+
}
52+
53+
@Override
54+
public @Nullable V put(K key, V value) {
55+
return entry(key.hashCode()).map(entry -> entry.value = value).orElseGet(() -> {
56+
var entry = new Entry(key, value);
57+
entries.add(entry);
58+
return entry.value;
59+
});
60+
}
61+
62+
@Override
63+
public V remove(Object key) {
64+
var result = entry(key.hashCode());
65+
if (result.isEmpty()) return null;
66+
var entry = result.get();
67+
if (!entries.remove(entry)) return null;
68+
return entry.value;
69+
}
70+
71+
@Override
72+
public void putAll(@NonNull Map<? extends K, ? extends V> m) {
73+
m.forEach(this::put);
74+
}
75+
76+
@Override
77+
public void clear() {
78+
entries.clear();
79+
}
80+
81+
@Override
82+
public @NonNull Set<K> keySet() {
83+
return entries.stream().map(Entry::getKey).collect(Collectors.toUnmodifiableSet());
84+
}
85+
86+
@Override
87+
public @NonNull Collection<V> values() {
88+
return entries.stream().map(Entry::getValue).collect(Collectors.toUnmodifiableSet());
89+
}
90+
91+
@Override
92+
public @NonNull Set<Map.Entry<K, V>> entrySet() {
93+
return Polyfill.uncheckedCast(entries);
94+
}
95+
96+
private Optional<Entry> entry(int keyCode) {
97+
return entries.stream().filter(e -> e.hashCode() == keyCode).findAny();
98+
}
99+
100+
@Value
101+
private class Entry implements Map.Entry<K, V> {
102+
K key;
103+
@NonFinal V value;
104+
105+
@Override
106+
public V setValue(V value) {
107+
var previous = this.value;
108+
this.value = value;
109+
return previous;
110+
}
111+
112+
@Override
113+
public int hashCode() {
114+
return key.hashCode();
115+
}
116+
117+
@Override
118+
@SuppressWarnings("EqualsDoesntCheckParameterClass")
119+
public boolean equals(Object obj) {
120+
return obj != null && hashCode() == obj.hashCode();
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)