|
1 | 1 | package org.embeddedt.modernfix.util; |
2 | 2 |
|
| 3 | +import com.google.common.base.Function; |
3 | 4 | import com.google.common.collect.Interner; |
4 | 5 | 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; |
8 | 7 |
|
9 | | -import java.util.*; |
10 | | -import java.util.function.Function; |
| 8 | +import java.util.HashMap; |
| 9 | +import java.util.Map; |
11 | 10 |
|
12 | 11 | /** |
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. |
15 | 13 | */ |
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> { |
20 | 15 | private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner(); |
21 | 16 |
|
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; |
53 | 19 | } |
54 | 20 |
|
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(); |
113 | 23 | } |
114 | 24 |
|
115 | | - @NotNull |
116 | 25 | @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); |
119 | 28 | } |
120 | 29 |
|
121 | 30 | @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); |
129 | 35 | } |
130 | 36 |
|
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); |
141 | 39 | } |
142 | 40 |
|
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; |
156 | 45 | } |
157 | 46 | } |
0 commit comments