forked from functionaljava/functionaljava
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTreeMap.java
More file actions
429 lines (385 loc) · 14 KB
/
TreeMap.java
File metadata and controls
429 lines (385 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
package fj.data;
import fj.Equal;
import fj.F;
import fj.F1Functions;
import fj.Hash;
import fj.Ord;
import fj.P;
import fj.P2;
import fj.P3;
import fj.Show;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import static fj.Function.compose;
import static fj.Function.flip;
import static fj.P.p;
import static fj.data.IterableW.join;
import static fj.data.List.iterableList;
/**
* An immutable, in-memory map, backed by a red-black tree.
*/
public final class TreeMap<K, V> implements Iterable<P2<K, V>> {
private final Set<P2<K, Option<V>>> tree;
private TreeMap(final Set<P2<K, Option<V>>> tree) {
this.tree = tree;
}
private static <K, V> Ord<P2<K, V>> ord(final Ord<K> keyOrd) {
return keyOrd.contramap(P2.__1());
}
/**
* Constructs an empty tree map.
*
* @param keyOrd An order for the keys of the tree map.
* @return an empty TreeMap with the given key order.
*/
public static <K, V> TreeMap<K, V> empty(final Ord<K> keyOrd) {
return new TreeMap<>(Set.empty(TreeMap.ord(keyOrd)));
}
@Override
public boolean equals(Object other) {
return Equal.equals0(TreeMap.class, this, other, () -> Equal.treeMapEqual(Equal.anyEqual(), Equal.anyEqual()));
}
@Override
public int hashCode() {
return Hash.treeMapHash(Hash.<K>anyHash(), Hash.<V>anyHash()).hash(this);
}
@Override
public String toString() {
return Show.treeMapShow(Show.<K>anyShow(), Show.<V>anyShow()).showS(this);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param p2s The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
@SafeVarargs public static <K, V> TreeMap<K, V> treeMap(final Ord<K> keyOrd, final P2<K, V>... p2s) {
return arrayTreeMap(keyOrd, p2s);
}
/**
* Constructs a tree map from the given elements.
*
* @deprecated As of release 4.5, use {@link #iterableTreeMap(Ord, Iterable)}
*
* @param keyOrd An order for the keys of the tree map.
* @param list The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
@Deprecated
public static <K, V> TreeMap<K, V> treeMap(final Ord<K> keyOrd, final List<P2<K, V>> list) {
return iterableTreeMap(keyOrd, list);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param it The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
public static <K, V> TreeMap<K, V> iterableTreeMap(final Ord<K> keyOrd, final Iterable<P2<K, V>> it) {
TreeMap<K, V> tm = empty(keyOrd);
for (final P2<K, V> p2 : it) {
tm = tm.set(p2._1(), p2._2());
}
return tm;
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param it The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
public static <K, V> TreeMap<K, V> iteratorTreeMap(final Ord<K> keyOrd, final Iterator<P2<K, V>> it) {
return iterableTreeMap(keyOrd, () -> it);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param ps The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
@SafeVarargs
public static <K, V> TreeMap<K, V> arrayTreeMap(final Ord<K> keyOrd, final P2<K, V>...ps) {
return iterableTreeMap(keyOrd, Array.array(ps));
}
/**
* Returns a potential value that the given key maps to.
*
* @param k The key to look up in the tree map.
* @return A potential value for the given key.
*/
public Option<V> get(final K k) {
return tree.lookup(p(k, Option.none())).bind(P2::_2);
}
/**
* Inserts the given key and value association into the tree map.
* If the given key is already mapped to a value, the old value is replaced with the given one.
*
* @param k The key to insert.
* @param v The value to insert.
* @return A new tree map with the given value mapped to the given key.
*/
public TreeMap<K, V> set(final K k, final V v) {
return new TreeMap<>(tree.insert(p(k, Option.some(v))));
}
/**
* Deletes the entry in the tree map that corresponds to the given key.
*
* @param k The key to delete from this tree map.
* @return A new tree map with the entry corresponding to the given key removed.
*/
public TreeMap<K, V> delete(final K k) {
return new TreeMap<>(tree.delete(p(k, Option.none())));
}
/**
* Returns the number of entries in this tree map.
*
* @return The number of entries in this tree map.
*/
public int size() {
return tree.size();
}
/**
* Determines if this tree map has any entries.
*
* @return <code>true</code> if this tree map has no entries, <code>false</code> otherwise.
*/
public boolean isEmpty() {
return tree.isEmpty();
}
/**
* Returns all values in this tree map.
*
* @return All values in this tree map.
*/
public List<V> values() {
return iterableList(join(tree.toList().map(compose(IterableW.wrap(), P2.__2()))));
}
/**
* Returns all keys in this tree map.
*
* @return All keys in this tree map.
*/
public List<K> keys() {
return tree.toList().map(P2.__1());
}
/**
* Determines if the given key value exists in this tree map.
*
* @param k The key value to look for in this tree map.
* @return <code>true</code> if this tree map contains the given key, <code>false</code> otherwise.
*/
public boolean contains(final K k) {
return tree.member(p(k, Option.none()));
}
/**
* Returns an iterator for this map's key-value pairs.
* This method exists to permit the use in a <code>for</code>-each loop.
*
* @return A iterator for this map's key-value pairs.
*/
public Iterator<P2<K, V>> iterator() {
return join(tree.toStream().map(P2.map2_(IterableW.wrap())
).map(P2.tuple(compose(IterableW.map(), P.p2())))).iterator();
}
/**
* A mutable map projection of this tree map.
*
* @return A new mutable map isomorphic to this tree map.
*/
public Map<K, V> toMutableMap() {
final F<K, P2<K, Option<V>>> fakePair = k -> p(k, Option.none());
final Comparator<K> comparator = tree.ord().contramap(fakePair).toComparator();
final Map<K, V> m = new java.util.TreeMap<>(comparator);
for (final P2<K, V> e : this) {
m.put(e._1(), e._2());
}
return m;
}
public Stream<P2<K, V>> toStream() {
return tree.toStream().map(p -> p.map2(o -> o.some()));
}
public Stream<P2<K, V>> toStreamReverse() {
return tree.toStreamReverse().map(p -> p.map2(o -> o.some()));
}
public List<P2<K, V>> toList() {
return tree.toList().map(p -> p.map2(o -> o.some()));
}
public List<P2<K, V>> toListReverse() {
return tree.toListReverse().map(p -> p.map2(o -> o.some()));
}
/**
* An immutable projection of the given mutable map.
*
* @param ord An order for the map's keys.
* @param m A mutable map to project to an immutable one.
* @return A new immutable tree map isomorphic to the given mutable map.
*/
public static <K, V> TreeMap<K, V> fromMutableMap(final Ord<K> ord, final Map<K, V> m) {
TreeMap<K, V> t = empty(ord);
for (final Map.Entry<K, V> e : m.entrySet()) {
t = t.set(e.getKey(), e.getValue());
}
return t;
}
/**
* Returns a first-class version of the get method for this TreeMap.
*
* @return a functional representation of this TreeMap.
*/
public F<K, Option<V>> get() {
return this::get;
}
/**
* Modifies the value for the given key, if present, by applying the given function to it.
*
* @param k The key for the value to modify.
* @param f A function with which to modify the value.
* @return A new tree map with the value for the given key transformed by the given function,
* paired with True if the map was modified, otherwise False.
*/
public P2<Boolean, TreeMap<K, V>> update(final K k, final F<V, V> f) {
final P2<Boolean, Set<P2<K, Option<V>>>> up =
tree.update(p(k, Option.none()), compose(P2.tuple(P.p2()), P2.map2_(Option.<V, V>map().f(f))));
return p(up._1(), new TreeMap<>(up._2()));
}
/**
* Modifies the value for the given key, if present, by applying the given function to it, or
* inserts the given value if the key is not present.
*
* @param k The key for the value to modify.
* @param f A function with which to modify the value.
* @param v A value to associate with the given key if the key is not already present.
* @return A new tree map with the value for the given key transformed by the given function.
*/
public TreeMap<K, V> update(final K k, final F<V, V> f, final V v) {
final P2<Boolean, TreeMap<K, V>> up = update(k, f);
return up._1() ? up._2() : set(k, v);
}
/**
* Splits this TreeMap at the given key. Returns a triple of:
* <ul>
* <li>A set containing all the values of this map associated with keys less than the given key.</li>
* <li>An option of a value mapped to the given key, if it exists in this map, otherwise None.
* <li>A set containing all the values of this map associated with keys greater than the given key.</li>
* </ul>
*
* @param k A key at which to split this map.
* @return Two sets and an optional value, where all elements in the first set are mapped to keys less than the given
* key in this map, all the elements in the second set are mapped to keys greater than the given key,
* and the optional value is the value associated with the given key if present, otherwise None.
*/
public P3<Set<V>, Option<V>, Set<V>> split(Ord<V> ord, final K k) {
final F<Set<P2<K, Option<V>>>, Set<V>> getSome = F1Functions.mapSet(F1Functions.o(Option.fromSome(), P2.__2()), ord);
return tree.split(p(k, Option.none())).map1(getSome).map3(getSome)
.map2(F1Functions.o(Option.join(), F1Functions.mapOption(P2.__2())));
}
/**
* Internal construction of a TreeMap from the given set.
* @param ord An order for the keys of the tree map.
* @param s The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
private static <K, V> TreeMap<K, V> treeMap(Ord<K> ord, Set<P2<K, Option<V>>> s) {
TreeMap<K, V> empty = TreeMap.empty(ord);
TreeMap<K, V> tree = s.toList().foldLeft((tm, p2) -> {
Option<V> opt = p2._2();
if (opt.isSome()) {
return tm.set(p2._1(), opt.some());
}
return tm;
}, empty);
return tree;
}
/**
* Splits this TreeMap at the given key. Returns a triple of:
* <ul>
* <li>A tree map containing all the values of this map associated with keys less than the given key.</li>
* <li>An option of a value mapped to the given key, if it exists in this map, otherwise None.
* <li>A tree map containing all the values of this map associated with keys greater than the given key.</li>
* </ul>
*
* @param k A key at which to split this map.
* @return Two tree maps and an optional value, where all keys in the first tree map are mapped
* to keys less than the given key in this map, all the keys in the second tree map are mapped
* to keys greater than the given key, and the optional value is the value associated with the
* given key if present, otherwise None.
*/
public P3<TreeMap<K, V>, Option<V>, TreeMap<K, V>> splitLookup(final K k) {
P3<Set<P2<K, Option<V>>>, Option<P2<K, Option<V>>>, Set<P2<K, Option<V>>>> p3 = tree.split(p(k, get(k)));
Ord<K> o = tree.ord().contramap(k2 -> p(k2, Option.none()));
return p(treeMap(o, p3._1()), get(k), treeMap(o, p3._3()));
}
/**
* Maps the given function across the values of this TreeMap.
*
* @param f A function to apply to the values of this TreeMap.
* @return A new TreeMap with the values transformed by the given function.
*/
@SuppressWarnings("unchecked")
public <W> TreeMap<K, W> map(final F<V, W> f) {
final F<P2<K, Option<V>>, P2<K, Option<W>>> g = compose(p2 -> p(p2._1(), p2._2()), P2.map2_(F1Functions.mapOption(f)));
final F<K, P2<K, Option<V>>> coord = flip(P.<K, Option<V>>p2()).f(Option.none());
final Ord<K> o = tree.ord().contramap(coord);
return new TreeMap<>(tree.map(TreeMap.ord(o), g));
}
/**
* Returns the minimum (key, value) pair in the tree if the tree is not empty.
*/
public Option<P2<K, V>> min() {
return tree.min().map(p -> p(p._1(), p._2().some()));
}
/**
* Returns the minimum key in the tree if the tree is not empty.
*/
public Option<K> minKey() {
return tree.min().map(P2::_1);
}
/**
* Returns the maximum (key, value) pair in the tree if the tree is not empty.
*/
public Option<P2<K, V>> max() {
return tree.max().map(p -> p(p._1(), p._2().some()));
}
/**
* Returns the maximum key in the tree if the tree is not empty.
*/
public Option<K> maxKey() {
return tree.max().map(P2::_1);
}
/**
* The expression <code>t1.union(t2)</code> takes the left-biased union of <code>t1</code>
* and <code>t2</code>. It prefers <code>t1</code> when duplicate keys are encountered.
*
* @param t2 The other tree we wish to combine with this one
* @return The combined TreeMap
*/
public TreeMap<K, V> union(TreeMap<K, V> t2) {
// TODO This could be implemented more efficiently using "hedge union"
TreeMap<K, V> result = t2;
for(P2<K,V> p : this) {
result = result.set(p._1(), p._2());
}
return result;
}
/**
* The expression <code>t1.union(t2)</code> takes the left-biased union of <code>t1</code>
* and <code>t2</code>. It prefers <code>t1</code> when duplicate keys are encountered.
*
* @param t2 The other list/set of pairs we wish to combine with this one
* @return The combined TreeMap
*/
public TreeMap<K, V> union(Iterable<P2<K, V>> t2) {
TreeMap<K, V> result = this;
for(P2<K,V> p : t2) {
if(!this.contains(p._1())) {
result = result.set(p._1(), p._2());
}
}
return result;
}
}