9
9
import java .lang .reflect .Array ;
10
10
import java .util .ArrayList ;
11
11
import java .util .Collection ;
12
+ import java .util .Collections ;
12
13
import java .util .Iterator ;
13
14
import java .util .LinkedHashMap ;
14
15
import java .util .List ;
18
19
import java .util .OptionalInt ;
19
20
import java .util .Set ;
20
21
import java .util .concurrent .CompletableFuture ;
21
- import java .util .function .BiFunction ;
22
22
import java .util .function .BinaryOperator ;
23
23
import java .util .function .Function ;
24
24
import java .util .function .Predicate ;
25
25
import java .util .function .Supplier ;
26
- import java .util .stream .Collectors ;
27
26
import java .util .stream .Stream ;
28
27
29
28
import static java .util .Collections .singletonList ;
30
- import static java .util .function .Function .identity ;
31
- import static java .util .stream .Collectors .mapping ;
32
29
33
30
@ Internal
34
31
public class FpKit {
35
32
36
33
//
37
34
// From a list of named things, get a map of them by name, merging them according to the merge function
38
35
public static <T > Map <String , T > getByName (List <T > namedObjects , Function <T , String > nameFn , BinaryOperator <T > mergeFunc ) {
39
- return namedObjects .stream ().collect (Collectors .toMap (
40
- nameFn ,
41
- identity (),
42
- mergeFunc ,
43
- LinkedHashMap ::new )
44
- );
36
+ return toMap (namedObjects , nameFn , mergeFunc );
37
+ }
38
+
39
+ //
40
+ // From a collection of keyed things, get a map of them by key, merging them according to the merge function
41
+ public static <T , NewKey > Map <NewKey , T > toMap (Collection <T > collection , Function <T , NewKey > keyFunction , BinaryOperator <T > mergeFunc ) {
42
+ Map <NewKey , T > resultMap = new LinkedHashMap <>();
43
+ for (T obj : collection ) {
44
+ NewKey key = keyFunction .apply (obj );
45
+ if (resultMap .containsKey (key )) {
46
+ T existingValue = resultMap .get (key );
47
+ T mergedValue = mergeFunc .apply (existingValue , obj );
48
+ resultMap .put (key , mergedValue );
49
+ } else {
50
+ resultMap .put (key , obj );
51
+ }
52
+ }
53
+ return resultMap ;
45
54
}
46
55
47
56
// normal groupingBy but with LinkedHashMap
48
57
public static <T , NewKey > Map <NewKey , ImmutableList <T >> groupingBy (Collection <T > list , Function <T , NewKey > function ) {
49
- return list . stream (). collect ( Collectors . groupingBy ( function , LinkedHashMap :: new , mapping ( Function . identity (), ImmutableList . toImmutableList ())) );
58
+ return filterAndGroupingBy ( list , ALWAYS_TRUE , function );
50
59
}
51
60
61
+ @ SuppressWarnings ("unchecked" )
52
62
public static <T , NewKey > Map <NewKey , ImmutableList <T >> filterAndGroupingBy (Collection <T > list ,
53
63
Predicate <? super T > predicate ,
54
64
Function <T , NewKey > function ) {
55
- return list .stream ().filter (predicate ).collect (Collectors .groupingBy (function , LinkedHashMap ::new , mapping (Function .identity (), ImmutableList .toImmutableList ())));
56
- }
65
+ //
66
+ // The cleanest version of this code would have two maps, one of immutable list builders and one
67
+ // of the built immutable lists. BUt we are trying to be performant and memory efficient so
68
+ // we treat it as a map of objects and cast like its Java 4x
69
+ //
70
+ Map <NewKey , Object > resutMap = new LinkedHashMap <>();
71
+ for (T item : list ) {
72
+ if (predicate .test (item )) {
73
+ NewKey key = function .apply (item );
74
+ // we have to use an immutable list builder as we built it out
75
+ ((ImmutableList .Builder <Object >) resutMap .computeIfAbsent (key , k -> ImmutableList .builder ()))
76
+ .add (item );
77
+ }
78
+ }
79
+ if (resutMap .isEmpty ()) {
80
+ return Collections .emptyMap ();
81
+ }
82
+ // Convert builders to ImmutableLists in place to avoid an extra allocation
83
+ // yes the code is yuck - but its more performant yuck!
84
+ resutMap .replaceAll ((key , builder ) ->
85
+ ((ImmutableList .Builder <Object >) builder ).build ());
57
86
58
- public static < T , NewKey > Map < NewKey , ImmutableList < T >> groupingBy ( Stream < T > stream , Function < T , NewKey > function ) {
59
- return stream . collect ( Collectors . groupingBy ( function , LinkedHashMap :: new , mapping ( Function . identity (), ImmutableList . toImmutableList ()))) ;
87
+ // make it the right shape - like as if generics were never invented
88
+ return ( Map < NewKey , ImmutableList < T >>) ( Map <?, ?>) resutMap ;
60
89
}
61
90
62
- public static <T , NewKey > Map <NewKey , T > groupingByUniqueKey (Collection <T > list , Function <T , NewKey > keyFunction ) {
63
- return list .stream ().collect (Collectors .toMap (
64
- keyFunction ,
65
- identity (),
66
- throwingMerger (),
67
- LinkedHashMap ::new )
68
- );
91
+ public static <T , NewKey > Map <NewKey , T > toMapByUniqueKey (Collection <T > list , Function <T , NewKey > keyFunction ) {
92
+ return toMap (list , keyFunction , throwingMerger ());
69
93
}
70
94
71
- public static <T , NewKey > Map <NewKey , T > groupingByUniqueKey (Stream <T > stream , Function <T , NewKey > keyFunction ) {
72
- return stream .collect (Collectors .toMap (
73
- keyFunction ,
74
- identity (),
75
- throwingMerger (),
76
- LinkedHashMap ::new )
77
- );
78
- }
95
+
96
+ private static final Predicate <Object > ALWAYS_TRUE = o -> true ;
97
+
98
+ private static final BinaryOperator <Object > THROWING_MERGER_SINGLETON = (u , v ) -> {
99
+ throw new IllegalStateException (String .format ("Duplicate key %s" , u ));
100
+ };
101
+
79
102
80
103
private static <T > BinaryOperator <T > throwingMerger () {
81
- return (u , v ) -> {
82
- throw new IllegalStateException (String .format ("Duplicate key %s" , u ));
83
- };
104
+ //noinspection unchecked
105
+ return (BinaryOperator <T >) THROWING_MERGER_SINGLETON ;
84
106
}
85
107
86
108
@@ -240,11 +262,6 @@ public static <T> List<T> valuesToList(Map<?, T> map) {
240
262
return new ArrayList <>(map .values ());
241
263
}
242
264
243
- public static <K , V , U > List <U > mapEntries (Map <K , V > map , BiFunction <K , V , U > function ) {
244
- return map .entrySet ().stream ().map (entry -> function .apply (entry .getKey (), entry .getValue ())).collect (Collectors .toList ());
245
- }
246
-
247
-
248
265
public static <T > List <List <T >> transposeMatrix (List <? extends List <T >> matrix ) {
249
266
int rowCount = matrix .size ();
250
267
int colCount = matrix .get (0 ).size ();
@@ -261,21 +278,13 @@ public static <T> List<List<T>> transposeMatrix(List<? extends List<T>> matrix)
261
278
return result ;
262
279
}
263
280
264
- public static <T > CompletableFuture <List <T >> flatList (CompletableFuture <List <List <T >>> cf ) {
265
- return cf .thenApply (FpKit ::flatList );
266
- }
267
-
268
- public static <T > List <T > flatList (Collection <List <T >> listLists ) {
269
- return listLists .stream ()
270
- .flatMap (List ::stream )
271
- .collect (ImmutableList .toImmutableList ());
272
- }
273
-
274
281
public static <T > Optional <T > findOne (Collection <T > list , Predicate <T > filter ) {
275
- return list
276
- .stream ()
277
- .filter (filter )
278
- .findFirst ();
282
+ for (T t : list ) {
283
+ if (filter .test (t )) {
284
+ return Optional .of (t );
285
+ }
286
+ }
287
+ return Optional .empty ();
279
288
}
280
289
281
290
public static <T > T findOneOrNull (List <T > list , Predicate <T > filter ) {
@@ -292,10 +301,13 @@ public static <T> int findIndex(List<T> list, Predicate<T> filter) {
292
301
}
293
302
294
303
public static <T > List <T > filterList (Collection <T > list , Predicate <T > filter ) {
295
- return list
296
- .stream ()
297
- .filter (filter )
298
- .collect (Collectors .toList ());
304
+ List <T > result = new ArrayList <>();
305
+ for (T t : list ) {
306
+ if (filter .test (t )) {
307
+ result .add (t );
308
+ }
309
+ }
310
+ return result ;
299
311
}
300
312
301
313
public static <T > Set <T > filterSet (Collection <T > input , Predicate <T > filter ) {
@@ -352,9 +364,10 @@ public static <T> Supplier<T> interThreadMemoize(Supplier<T> delegate) {
352
364
/**
353
365
* Faster set intersection.
354
366
*
355
- * @param <T> for two
367
+ * @param <T> for two
356
368
* @param set1 first set
357
369
* @param set2 second set
370
+ *
358
371
* @return intersection set
359
372
*/
360
373
public static <T > Set <T > intersection (Set <T > set1 , Set <T > set2 ) {
0 commit comments