0
# Functional Programming Support
1
2
Apache Commons Collections provides comprehensive support for functional-style programming through predicates, transformers, closures, and factories. These functional interfaces enable powerful collection processing and manipulation patterns.
3
4
## Core Functional Interfaces
5
6
### Predicate<T> Interface
7
8
Predicates test objects and return boolean results, used for filtering and validation.
9
10
```java { .api }
11
import org.apache.commons.collections4.Predicate;
12
import org.apache.commons.collections4.PredicateUtils;
13
import org.apache.commons.collections4.CollectionUtils;
14
import java.util.List;
15
import java.util.Arrays;
16
import java.util.Collection;
17
18
// Create custom predicate
19
Predicate<Integer> evenPredicate = n -> n % 2 == 0;
20
Predicate<String> longStringPredicate = s -> s.length() > 5;
21
22
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
23
List<String> words = Arrays.asList("cat", "elephant", "dog", "butterfly", "ant", "hippopotamus");
24
25
// Filter collections using predicates
26
Collection<Integer> evenNumbers = CollectionUtils.select(numbers, evenPredicate);
27
// Result: [2, 4, 6, 8, 10]
28
29
Collection<String> longWords = CollectionUtils.select(words, longStringPredicate);
30
// Result: ["elephant", "butterfly", "hippopotamus"]
31
32
// Test single objects
33
boolean isEven = evenPredicate.evaluate(4); // true
34
boolean isLong = longStringPredicate.evaluate("cat"); // false
35
```
36
37
### Transformer<I, O> Interface
38
39
Transformers convert objects from one type or state to another.
40
41
```java { .api }
42
import org.apache.commons.collections4.Transformer;
43
import org.apache.commons.collections4.TransformerUtils;
44
import java.util.Collection;
45
46
// Simple transformations
47
Transformer<String, String> upperCase = String::toUpperCase;
48
Transformer<String, Integer> stringLength = String::length;
49
Transformer<Integer, String> numberToString = Object::toString;
50
51
List<String> words = Arrays.asList("hello", "world", "java", "collections");
52
53
// Transform collections
54
Collection<String> upperCaseWords = CollectionUtils.collect(words, upperCase);
55
// Result: ["HELLO", "WORLD", "JAVA", "COLLECTIONS"]
56
57
Collection<Integer> wordLengths = CollectionUtils.collect(words, stringLength);
58
// Result: [5, 5, 4, 11]
59
60
// Transform single objects
61
String upper = upperCase.transform("hello"); // "HELLO"
62
Integer length = stringLength.transform("test"); // 4
63
```
64
65
### Closure<T> Interface
66
67
Closures perform actions on objects without returning values (side effects).
68
69
```java { .api }
70
import org.apache.commons.collections4.Closure;
71
import org.apache.commons.collections4.ClosureUtils;
72
import org.apache.commons.collections4.IterableUtils;
73
74
// Create closures for side effects
75
Closure<String> printClosure = System.out::println;
76
Closure<Integer> squarePrintClosure = n -> System.out.println(n + "² = " + (n * n));
77
78
List<String> messages = Arrays.asList("Hello", "World", "Java");
79
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
80
81
// Apply closure to each element
82
IterableUtils.forEach(messages, printClosure);
83
// Output: Hello, World, Java (each on new line)
84
85
IterableUtils.forEach(numbers, squarePrintClosure);
86
// Output: 1² = 1, 2² = 4, 3² = 9, 4² = 16, 5² = 25
87
88
// Execute closure on single object
89
printClosure.execute("Single message");
90
```
91
92
### Factory<T> Interface
93
94
Factories create new objects on demand.
95
96
```java { .api }
97
import org.apache.commons.collections4.Factory;
98
import org.apache.commons.collections4.FactoryUtils;
99
import org.apache.commons.collections4.map.LazyMap;
100
import java.util.Map;
101
import java.util.HashMap;
102
import java.util.ArrayList;
103
import java.util.List;
104
import java.time.LocalDateTime;
105
106
// Simple factories
107
Factory<String> constantFactory = () -> "default";
108
Factory<List<String>> listFactory = ArrayList::new;
109
Factory<LocalDateTime> timestampFactory = LocalDateTime::now;
110
111
// Use factory to create objects
112
String defaultValue = constantFactory.create(); // "default"
113
List<String> newList = listFactory.create(); // new ArrayList<>()
114
LocalDateTime now = timestampFactory.create(); // current timestamp
115
116
// Lazy map with factory
117
Map<String, List<String>> lazyMap = LazyMap.lazyMap(new HashMap<>(), listFactory);
118
119
// Lists are created on-demand
120
lazyMap.get("key1").add("value1"); // Creates new ArrayList automatically
121
lazyMap.get("key2").add("value2"); // Creates another new ArrayList
122
```
123
124
## Predicate Utilities
125
126
### Built-in Predicates
127
128
```java { .api }
129
import org.apache.commons.collections4.PredicateUtils;
130
131
// Common predicates
132
Predicate<Object> nullPred = PredicateUtils.nullPredicate();
133
Predicate<Object> notNullPred = PredicateUtils.notNullPredicate();
134
Predicate<Object> truePred = PredicateUtils.truePredicate();
135
Predicate<Object> falsePred = PredicateUtils.falsePredicate();
136
137
// Equality predicates
138
Predicate<String> equalToHello = PredicateUtils.equalPredicate("hello");
139
Predicate<Integer> equalToTen = PredicateUtils.equalPredicate(10);
140
141
// Instance type predicate
142
Predicate<Object> stringInstancePred = PredicateUtils.instanceofPredicate(String.class);
143
Predicate<Object> numberInstancePred = PredicateUtils.instanceofPredicate(Number.class);
144
145
// Test objects
146
List<Object> mixed = Arrays.asList("hello", 42, null, "world", 3.14, null);
147
148
Collection<Object> notNulls = CollectionUtils.select(mixed, notNullPred);
149
// Result: ["hello", 42, "world", 3.14]
150
151
Collection<Object> strings = CollectionUtils.select(mixed, stringInstancePred);
152
// Result: ["hello", "world"]
153
```
154
155
### Combining Predicates
156
157
```java { .api }
158
// Logical operations
159
Predicate<Integer> positive = n -> n > 0;
160
Predicate<Integer> lessThanTen = n -> n < 10;
161
Predicate<Integer> even = n -> n % 2 == 0;
162
163
// AND combination
164
Predicate<Integer> positiveAndEven = PredicateUtils.andPredicate(positive, even);
165
Predicate<Integer> singleDigitPositive = PredicateUtils.andPredicate(positive, lessThanTen);
166
167
// OR combination
168
Predicate<Integer> positiveOrEven = PredicateUtils.orPredicate(positive, even);
169
170
// NOT operation
171
Predicate<Integer> notEven = PredicateUtils.notPredicate(even); // Odd numbers
172
173
// Complex combinations
174
Predicate<Integer> complexPredicate = PredicateUtils.andPredicate(
175
positiveAndEven,
176
lessThanTen
177
); // Positive, even, single-digit numbers
178
179
List<Integer> numbers = Arrays.asList(-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
180
181
Collection<Integer> positiveEvenSingleDigit = CollectionUtils.select(numbers, complexPredicate);
182
// Result: [2, 4, 6, 8]
183
```
184
185
### Unique Predicate
186
187
```java { .api }
188
// Only accepts each object once
189
Predicate<String> uniquePred = PredicateUtils.uniquePredicate();
190
191
List<String> withDuplicates = Arrays.asList("a", "b", "a", "c", "b", "d", "a");
192
Collection<String> unique = CollectionUtils.select(withDuplicates, uniquePred);
193
// Result: ["a", "b", "c", "d"] (first occurrence of each)
194
195
// Reset by creating new predicate
196
Predicate<String> anotherUnique = PredicateUtils.uniquePredicate();
197
// This one has fresh state
198
```
199
200
### Invoker Predicate
201
202
```java { .api }
203
// Invoke method and test result
204
Predicate<String> isEmptyPred = PredicateUtils.invokerPredicate("isEmpty");
205
Predicate<List<?>> isListEmptyPred = PredicateUtils.invokerPredicate("isEmpty");
206
207
List<String> testStrings = Arrays.asList("", "hello", "", "world", "");
208
Collection<String> emptyStrings = CollectionUtils.select(testStrings, isEmptyPred);
209
// Result: ["", "", ""]
210
211
// Method with parameters
212
Predicate<String> startsWithHello = PredicateUtils.invokerPredicate(
213
"startsWith",
214
new Class[]{String.class},
215
new Object[]{"hello"}
216
);
217
```
218
219
## Transformer Utilities
220
221
### Built-in Transformers
222
223
```java { .api }
224
import org.apache.commons.collections4.TransformerUtils;
225
226
// Identity transformer (returns input unchanged)
227
Transformer<String, String> identity = TransformerUtils.nopTransformer();
228
229
// Constant transformer (always returns same value)
230
Transformer<Object, String> constant = TransformerUtils.constantTransformer("constant");
231
232
// Null transformer (always returns null)
233
Transformer<Object, Object> nullTransformer = TransformerUtils.nullTransformer();
234
235
// Class transformer (returns object's class)
236
Transformer<Object, Class<?>> classTransformer = TransformerUtils.asTransformer(Object.class);
237
238
// String representation
239
Transformer<Object, String> stringTransformer = TransformerUtils.stringValueTransformer();
240
241
List<Object> objects = Arrays.asList(42, "hello", 3.14, true);
242
Collection<String> strings = CollectionUtils.collect(objects, stringTransformer);
243
// Result: ["42", "hello", "3.14", "true"]
244
245
Collection<Class<?>> classes = CollectionUtils.collect(objects, classTransformer);
246
// Result: [Integer.class, String.class, Double.class, Boolean.class]
247
```
248
249
### Chaining Transformers
250
251
```java { .api }
252
// Chain multiple transformers
253
Transformer<String, String> trim = String::trim;
254
Transformer<String, String> upperCase = String::toUpperCase;
255
Transformer<String, Integer> length = String::length;
256
257
// Chain string operations
258
Transformer<String, String> trimAndUpper = TransformerUtils.chainedTransformer(trim, upperCase);
259
Transformer<String, Integer> trimUpperLength = TransformerUtils.chainedTransformer(
260
trim, upperCase, length
261
);
262
263
List<String> messy = Arrays.asList(" hello ", " WORLD ", " Java ");
264
265
Collection<String> cleaned = CollectionUtils.collect(messy, trimAndUpper);
266
// Result: ["HELLO", "WORLD", "JAVA"]
267
268
Collection<Integer> lengths = CollectionUtils.collect(messy, trimUpperLength);
269
// Result: [5, 5, 4]
270
```
271
272
### Map-based Transformer
273
274
```java { .api }
275
// Transform using lookup map
276
Map<String, String> countryToCapital = Map.of(
277
"USA", "Washington D.C.",
278
"France", "Paris",
279
"Japan", "Tokyo",
280
"Germany", "Berlin"
281
);
282
283
Transformer<String, String> capitalLookup = TransformerUtils.mapTransformer(countryToCapital);
284
285
List<String> countries = Arrays.asList("USA", "France", "Japan");
286
Collection<String> capitals = CollectionUtils.collect(countries, capitalLookup);
287
// Result: ["Washington D.C.", "Paris", "Tokyo"]
288
```
289
290
### Conditional Transformers
291
292
```java { .api }
293
// If-then-else transformer
294
Predicate<Integer> isPositive = n -> n > 0;
295
Transformer<Integer, String> positiveTransform = n -> "positive: " + n;
296
Transformer<Integer, String> negativeTransform = n -> "negative: " + n;
297
298
Transformer<Integer, String> conditionalTransformer = TransformerUtils.ifTransformer(
299
isPositive,
300
positiveTransform,
301
negativeTransform
302
);
303
304
List<Integer> numbers = Arrays.asList(-5, -1, 0, 1, 5);
305
Collection<String> results = CollectionUtils.collect(numbers, conditionalTransformer);
306
// Result: ["negative: -5", "negative: -1", "negative: 0", "positive: 1", "positive: 5"]
307
308
// Switch transformer with multiple conditions
309
Predicate<Integer> isZero = n -> n == 0;
310
Predicate<Integer> isPositive2 = n -> n > 0;
311
Transformer<Integer, String> zeroTransform = n -> "zero";
312
Transformer<Integer, String> defaultTransform = n -> "unknown";
313
314
Transformer<Integer, String> switchTransformer = TransformerUtils.switchTransformer(
315
new Predicate[]{isZero, isPositive2},
316
new Transformer[]{zeroTransform, positiveTransform},
317
defaultTransform
318
);
319
```
320
321
## Closure Utilities
322
323
### Built-in Closures
324
325
```java { .api }
326
import org.apache.commons.collections4.ClosureUtils;
327
328
// No-operation closure (does nothing)
329
Closure<Object> nopClosure = ClosureUtils.nopClosure();
330
331
// Exception closure (always throws exception)
332
Closure<Object> exceptionClosure = ClosureUtils.exceptionClosure();
333
334
// Invoker closure (calls method on object)
335
Closure<StringBuilder> appendClosure = ClosureUtils.invokerClosure("append",
336
new Class[]{String.class}, new Object[]{" - processed"});
337
338
List<StringBuilder> builders = Arrays.asList(
339
new StringBuilder("item1"),
340
new StringBuilder("item2"),
341
new StringBuilder("item3")
342
);
343
344
IterableUtils.forEach(builders, appendClosure);
345
// Each StringBuilder now has " - processed" appended
346
```
347
348
### Chaining Closures
349
350
```java { .api }
351
// Chain multiple closures
352
Closure<List<String>> addHello = list -> list.add("Hello");
353
Closure<List<String>> addWorld = list -> list.add("World");
354
Closure<List<String>> printSize = list -> System.out.println("Size: " + list.size());
355
356
Closure<List<String>> chainedClosure = ClosureUtils.chainedClosure(
357
addHello, addWorld, printSize
358
);
359
360
List<String> testList = new ArrayList<>();
361
chainedClosure.execute(testList);
362
// testList now contains ["Hello", "World"] and prints "Size: 2"
363
```
364
365
### For and While Closures
366
367
```java { .api }
368
// Execute closure N times
369
Closure<StringBuilder> appendStar = sb -> sb.append("*");
370
Closure<StringBuilder> repeatStar = ClosureUtils.forClosure(5, appendStar);
371
372
StringBuilder sb = new StringBuilder();
373
repeatStar.execute(sb);
374
System.out.println(sb.toString()); // "*****"
375
376
// While closure (execute while condition is true)
377
class Counter {
378
private int count = 0;
379
public int getCount() { return count; }
380
public void increment() { count++; }
381
}
382
383
Counter counter = new Counter();
384
Predicate<Counter> lessThanFive = c -> c.getCount() < 5;
385
Closure<Counter> increment = Counter::increment;
386
387
Closure<Counter> whileClosure = ClosureUtils.whileClosure(lessThanFive, increment);
388
whileClosure.execute(counter);
389
System.out.println(counter.getCount()); // 5
390
```
391
392
## Factory Utilities
393
394
### Built-in Factories
395
396
```java { .api }
397
import org.apache.commons.collections4.FactoryUtils;
398
import java.util.Date;
399
400
// Constant factory
401
Factory<String> constantStringFactory = FactoryUtils.constantFactory("default");
402
403
// Null factory
404
Factory<Object> nullFactory = FactoryUtils.nullFactory();
405
406
// Exception factory
407
Factory<Object> exceptionFactory = FactoryUtils.exceptionFactory();
408
409
// Prototype factory (clones prototype)
410
List<String> prototype = Arrays.asList("template", "item");
411
Factory<List<String>> prototypeFactory = FactoryUtils.prototypeFactory(prototype);
412
413
List<String> copy1 = prototypeFactory.create(); // Independent copy
414
List<String> copy2 = prototypeFactory.create(); // Another independent copy
415
416
// Instantiate factory (creates new instances via constructor)
417
Factory<Date> dateFactory = FactoryUtils.instantiateFactory(Date.class);
418
Date now1 = dateFactory.create(); // new Date()
419
Date now2 = dateFactory.create(); // another new Date()
420
421
// Instantiate with constructor parameters
422
Factory<StringBuilder> sbFactory = FactoryUtils.instantiateFactory(
423
StringBuilder.class,
424
new Class[]{String.class},
425
new Object[]{"initial"}
426
);
427
StringBuilder sb1 = sbFactory.create(); // new StringBuilder("initial")
428
```
429
430
## Advanced Functional Patterns
431
432
### Pipeline Processing
433
434
```java { .api }
435
public class DataProcessor {
436
// Define processing pipeline with functors
437
private final Predicate<String> validator = s -> s != null && !s.trim().isEmpty();
438
private final Transformer<String, String> cleaner = s -> s.trim().toLowerCase();
439
private final Transformer<String, String> formatter = s -> "processed: " + s;
440
private final Closure<String> logger = s -> System.out.println("Processed: " + s);
441
442
public List<String> processData(List<String> rawData) {
443
return rawData.stream()
444
.filter(validator::evaluate) // Validate
445
.map(cleaner::transform) // Clean
446
.map(formatter::transform) // Format
447
.peek(logger::execute) // Log (side effect)
448
.collect(Collectors.toList());
449
}
450
}
451
```
452
453
### Command Pattern with Closures
454
455
```java { .api }
456
public class CommandProcessor {
457
private final Map<String, Closure<String>> commands = new HashMap<>();
458
459
public CommandProcessor() {
460
// Register commands as closures
461
commands.put("uppercase", String::toUpperCase);
462
commands.put("print", System.out::println);
463
commands.put("reverse", s -> new StringBuilder(s).reverse().toString());
464
}
465
466
public void executeCommand(String commandName, String input) {
467
Closure<String> command = commands.get(commandName);
468
if (command != null) {
469
command.execute(input);
470
}
471
}
472
473
public void registerCommand(String name, Closure<String> command) {
474
commands.put(name, command);
475
}
476
}
477
```
478
479
### Strategy Pattern with Functors
480
481
```java { .api }
482
public class TextProcessor {
483
public enum ProcessingStrategy {
484
UPPERCASE(String::toUpperCase),
485
LOWERCASE(String::toLowerCase),
486
CAPITALIZE(s -> s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase()),
487
REVERSE(s -> new StringBuilder(s).reverse().toString());
488
489
private final Transformer<String, String> transformer;
490
491
ProcessingStrategy(Transformer<String, String> transformer) {
492
this.transformer = transformer;
493
}
494
495
public String process(String input) {
496
return transformer.transform(input);
497
}
498
}
499
500
public List<String> processTexts(List<String> texts, ProcessingStrategy strategy) {
501
return CollectionUtils.collect(texts, strategy.transformer);
502
}
503
}
504
```
505
506
### Functional Validation
507
508
```java { .api }
509
public class ValidationFramework {
510
public static class ValidationResult {
511
private final boolean valid;
512
private final List<String> errors;
513
514
// Constructor and methods...
515
}
516
517
public static <T> ValidationResult validate(T object, Predicate<T>... validators) {
518
List<String> errors = new ArrayList<>();
519
520
for (int i = 0; i < validators.length; i++) {
521
if (!validators[i].evaluate(object)) {
522
errors.add("Validation " + i + " failed");
523
}
524
}
525
526
return new ValidationResult(errors.isEmpty(), errors);
527
}
528
529
// Usage example
530
public ValidationResult validateUser(User user) {
531
return validate(user,
532
u -> u.getName() != null && !u.getName().trim().isEmpty(), // Name required
533
u -> u.getAge() >= 18, // Adult
534
u -> u.getEmail() != null && u.getEmail().contains("@") // Valid email
535
);
536
}
537
}
538
```
539
540
## Performance Considerations
541
542
### Efficient Predicate Composition
543
544
```java { .api }
545
// Efficient: short-circuit evaluation
546
Predicate<String> efficientPredicate = PredicateUtils.andPredicate(
547
s -> s != null, // Fast null check first
548
s -> s.length() > 10, // Then length check
549
s -> s.contains("expensive") // Most expensive check last
550
);
551
552
// Less efficient: expensive operations first
553
Predicate<String> inefficientPredicate = PredicateUtils.andPredicate(
554
s -> expensiveOperation(s), // Expensive operation first
555
s -> s != null // Null check last (may never execute)
556
);
557
```
558
559
### Reusable Functors
560
561
```java { .api }
562
// Create reusable functors to avoid recreation overhead
563
public class CommonFunctors {
564
public static final Predicate<String> NOT_EMPTY =
565
s -> s != null && !s.trim().isEmpty();
566
567
public static final Transformer<String, String> TRIM_UPPERCASE =
568
s -> s == null ? null : s.trim().toUpperCase();
569
570
public static final Factory<List<String>> STRING_LIST_FACTORY =
571
ArrayList::new;
572
573
// Use in multiple contexts
574
public static Collection<String> cleanStrings(Collection<String> input) {
575
return CollectionUtils.collect(
576
CollectionUtils.select(input, NOT_EMPTY),
577
TRIM_UPPERCASE
578
);
579
}
580
}
581
```
582
583
### Memory-Efficient Processing
584
585
```java { .api }
586
// Memory-efficient: process items one at a time
587
public void processLargeDataset(Iterable<String> largeDataset) {
588
Predicate<String> filter = s -> s.startsWith("important");
589
Transformer<String, String> transform = String::toUpperCase;
590
Closure<String> process = this::processItem;
591
592
// Memory-efficient streaming approach
593
for (String item : largeDataset) {
594
if (filter.evaluate(item)) {
595
String transformed = transform.transform(item);
596
process.execute(transformed);
597
}
598
}
599
}
600
601
// Less memory-efficient: creates intermediate collections
602
public void processLargeDatasetInefficient(Collection<String> largeDataset) {
603
Collection<String> filtered = CollectionUtils.select(largeDataset, filter); // Full collection in memory
604
Collection<String> transformed = CollectionUtils.collect(filtered, transform); // Another full collection
605
IterableUtils.forEach(transformed, process); // Process all at once
606
}
607
```
608
609
## Best Practices
610
611
### Type Safety
612
613
```java { .api }
614
// Use specific generic types
615
Predicate<String> stringPredicate = s -> s.length() > 0; // Good
616
Predicate<Object> objectPredicate = o -> o.toString().length() > 0; // Less safe
617
618
// Avoid raw types
619
Predicate rawPredicate = s -> true; // Don't do this
620
```
621
622
### Error Handling
623
624
```java { .api }
625
// Safe predicate that handles nulls
626
Predicate<String> safeStringPredicate = s -> {
627
try {
628
return s != null && s.trim().length() > 0;
629
} catch (Exception e) {
630
return false; // Safe default
631
}
632
};
633
634
// Safe transformer with error handling
635
Transformer<String, Integer> safeParseInt = s -> {
636
try {
637
return s != null ? Integer.parseInt(s.trim()) : 0;
638
} catch (NumberFormatException e) {
639
return 0; // Safe default
640
}
641
};
642
```
643
644
Functional programming support in Apache Commons Collections enables powerful, composable operations on collections while maintaining type safety and performance. Use these patterns to create clean, reusable, and maintainable code.