Skip to content

Commit 5d65665

Browse files
committed
Rewrite CanonizingStringMap to simply use a normal HashMap and intern keys
There are no memory savings from using the fastutil maps, and they may be harming performance based on the Project MMO issues Probably also the solution to #134
1 parent c1acdf1 commit 5d65665

File tree

1 file changed

+22
-133
lines changed

1 file changed

+22
-133
lines changed
Lines changed: 22 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,46 @@
11
package org.embeddedt.modernfix.util;
22

3+
import com.google.common.base.Function;
34
import com.google.common.collect.Interner;
45
import com.google.common.collect.Interners;
5-
import it.unimi.dsi.fastutil.objects.*;
6-
import org.jetbrains.annotations.NotNull;
7-
import org.jetbrains.annotations.Nullable;
6+
import com.google.common.collect.Maps;
87

9-
import java.util.*;
10-
import java.util.function.Function;
8+
import java.util.HashMap;
9+
import java.util.Map;
1110

1211
/**
13-
* Replacement backing map for CompoundTags. Uses an array map for tags with 4 or less entries,
14-
* and a hash map for larger tags.
12+
* Replacement backing map for CompoundTags that interns keys.
1513
*/
16-
public class CanonizingStringMap<T> implements Map<String, T> {
17-
private Object2ObjectMap<String, T> backingMap;
18-
19-
private static final int GROWTH_THRESHOLD = 4;
14+
public class CanonizingStringMap<T> extends HashMap<String, T> {
2015
private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner();
2116

22-
public CanonizingStringMap() {
23-
this(new Object2ObjectArrayMap<>());
24-
}
25-
26-
protected CanonizingStringMap(Object2ObjectMap<String, T> newMap) {
27-
this.backingMap = newMap;
28-
}
29-
30-
@Override
31-
public int size() {
32-
return backingMap.size();
33-
}
34-
35-
@Override
36-
public boolean isEmpty() {
37-
return backingMap.isEmpty();
38-
}
39-
40-
@Override
41-
public boolean containsKey(Object o) {
42-
return backingMap.containsKey(o);
43-
}
44-
45-
@Override
46-
public boolean containsValue(Object o) {
47-
return backingMap.containsValue(o);
48-
}
49-
50-
@Override
51-
public T get(Object o) {
52-
return backingMap.get(o);
17+
private static String intern(String key) {
18+
return key != null ? KEY_INTERNER.intern(key) : null;
5319
}
5420

55-
@Nullable
56-
@Override
57-
public T put(String s, T t) {
58-
if(backingMap.size() >= GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap) && !backingMap.containsKey(s)) {
59-
// map will grow to GROWTH_THRESHOLD + 1 entries, change to hashmap
60-
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
61-
}
62-
s = KEY_INTERNER.intern(s);
63-
return backingMap.put(s, t);
64-
}
65-
66-
@Override
67-
public T remove(Object o) {
68-
T value = backingMap.remove(o);
69-
// need to shrink to be consistent with new maps
70-
if(backingMap.size() <= GROWTH_THRESHOLD && backingMap instanceof Object2ObjectOpenHashMap) {
71-
backingMap = new Object2ObjectArrayMap<>(backingMap);
72-
}
73-
return value;
74-
}
75-
76-
@Override
77-
public void putAll(@NotNull Map<? extends String, ? extends T> map) {
78-
if(map.size() == 0)
79-
return;
80-
// grow early if we know there are enough non-overlapping keys
81-
if((map.size() - backingMap.size()) > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) {
82-
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
83-
}
84-
map.forEach((String key, T val) -> {
85-
key = KEY_INTERNER.intern(key);
86-
backingMap.put(key, val);
87-
});
88-
// if it's still an array, and now too big, grow it
89-
if(backingMap.size() > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) {
90-
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
91-
}
92-
}
93-
94-
@Override
95-
public void clear() {
96-
if(!(this.backingMap instanceof Object2ObjectArrayMap))
97-
this.backingMap = new Object2ObjectArrayMap<>();
98-
else
99-
this.backingMap.clear();
100-
}
101-
102-
@NotNull
103-
@Override
104-
public Set<String> keySet() {
105-
// has to be modifiable because mods (cough, Tinkers) use it to clear the tag
106-
return this.backingMap.keySet();
107-
}
108-
109-
@NotNull
110-
@Override
111-
public Collection<T> values() {
112-
return Collections.unmodifiableCollection(this.backingMap.values());
21+
public CanonizingStringMap() {
22+
super();
11323
}
11424

115-
@NotNull
11625
@Override
117-
public Set<Entry<String, T>> entrySet() {
118-
return Collections.unmodifiableSet(this.backingMap.entrySet());
26+
public T put(String key, T value) {
27+
return super.put(intern(key), value);
11928
}
12029

12130
@Override
122-
public boolean equals(Object o) {
123-
if (this == o) return true;
124-
if (o == null || getClass() != o.getClass()) return false;
125-
CanonizingStringMap<?> that = (CanonizingStringMap<?>)o;
126-
if(that.backingMap.size() != backingMap.size())
127-
return false;
128-
return backingMap.object2ObjectEntrySet().containsAll(that.backingMap.object2ObjectEntrySet());
31+
public void putAll(Map<? extends String, ? extends T> m) {
32+
HashMap<String, T> tmp = new HashMap<>();
33+
m.forEach((k, v) -> tmp.put(intern(k), v));
34+
super.putAll(tmp);
12935
}
13036

131-
/**
132-
* We deliberately use a hashcode that will be consistent regardless of underlying map type.
133-
*/
134-
@Override
135-
public int hashCode() {
136-
final ObjectIterator<Object2ObjectMap.Entry<String, T>> i = Object2ObjectMaps.fastIterator(backingMap);
137-
int h = 0, n = backingMap.size();
138-
while (n-- != 0)
139-
h += i.next().hashCode();
140-
return h;
37+
private void putAllWithoutInterning(Map<? extends String, ? extends T> m) {
38+
super.putAll(m);
14139
}
14240

143-
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> inputMap, Function<T, T> deepCopier) {
144-
Objects.requireNonNull(deepCopier);
145-
Object2ObjectMap<String, T> copiedBackingMap;
146-
int size = inputMap.backingMap.size();
147-
if(size > GROWTH_THRESHOLD) {
148-
copiedBackingMap = new Object2ObjectOpenHashMap<>(size);
149-
} else
150-
copiedBackingMap = new Object2ObjectArrayMap<>(size);
151-
inputMap.backingMap.object2ObjectEntrySet().forEach(entry -> {
152-
if(entry.getKey() != null && entry.getValue() != null)
153-
copiedBackingMap.put(entry.getKey(), deepCopier.apply(entry.getValue()));
154-
});
155-
return new CanonizingStringMap<>(copiedBackingMap);
41+
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> incomingMap, Function<T, T> deepCopier) {
42+
CanonizingStringMap<T> newMap = new CanonizingStringMap<>();
43+
newMap.putAllWithoutInterning(Maps.transformValues(incomingMap, deepCopier));
44+
return newMap;
15645
}
15746
}

0 commit comments

Comments
 (0)