• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 353

16 Jan 2026 01:01PM UTC coverage: 78.957% (-0.02%) from 78.976%
353

push

github

adangel
[core] Fix #6184: More consistent enum properties (#6233)

18529 of 24342 branches covered (76.12%)

Branch coverage included in aggregate %.

146 of 164 new or added lines in 20 files covered. (89.02%)

6 existing lines in 5 files now uncovered.

40349 of 50228 relevant lines covered (80.33%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.12
/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.util;
6

7
import static java.util.Arrays.asList;
8
import static java.util.Collections.emptyIterator;
9
import static java.util.Collections.emptyMap;
10
import static java.util.Collections.emptySet;
11

12
import java.util.AbstractMap;
13
import java.util.AbstractSet;
14
import java.util.ArrayList;
15
import java.util.Collection;
16
import java.util.Collections;
17
import java.util.EnumSet;
18
import java.util.HashMap;
19
import java.util.Iterator;
20
import java.util.LinkedHashMap;
21
import java.util.LinkedHashSet;
22
import java.util.List;
23
import java.util.Map;
24
import java.util.Map.Entry;
25
import java.util.Objects;
26
import java.util.Set;
27
import java.util.function.BiConsumer;
28
import java.util.function.BiFunction;
29
import java.util.function.BinaryOperator;
30
import java.util.function.Consumer;
31
import java.util.function.Function;
32
import java.util.function.Predicate;
33
import java.util.stream.Collector;
34
import java.util.stream.Collector.Characteristics;
35
import java.util.stream.Collectors;
36

37
import org.apache.commons.lang3.Validate;
38
import org.checkerframework.checker.nullness.qual.NonNull;
39
import org.checkerframework.checker.nullness.qual.Nullable;
40
import org.pcollections.ConsPStack;
41
import org.pcollections.HashTreePSet;
42
import org.pcollections.PMap;
43
import org.pcollections.PSequence;
44
import org.pcollections.PSet;
45

46
import net.sourceforge.pmd.lang.document.Chars;
47

48
/**
49
 * Generic collection-related utility functions for java.util types.
50
 *
51
 * @author Brian Remedios
52
 * @author Clément Fournier
53
 */
54
public final class CollectionUtil {
55

56
    private static final int UNKNOWN_SIZE = -1;
57

58
    private CollectionUtil() {
59
    }
60

61
    /**
62
     * Returns a list view that pretends it is the concatenation of
63
     * both lists. The returned view is unmodifiable. The implementation
64
     * is pretty stupid and not optimized for repeated concatenation,
65
     * but should be ok for smallish chains of random-access lists.
66
     *
67
     * @param head Head elements (to the left)
68
     * @param tail Tail elements (to the right)
69
     * @param <T>  Type of elements in both lists
70
     *
71
     * @return A concatenated view
72
     */
73
    public static <T> List<T> concatView(List<? extends T> head, List<? extends T> tail) {
74
        if (head.isEmpty()) {
×
75
            return makeUnmodifiableAndNonNull(tail);
×
76
        } else if (tail.isEmpty()) {
×
77
            return makeUnmodifiableAndNonNull(head);
×
78
        } else {
79
            return new ConsList<>(head, tail);
×
80
        }
81
    }
82

83
    /**
84
     * Returns a view over the given map, where values are mapped using the given function.
85
     * @since 7.14.0
86
     */
87
    public static <K, V, U> Map<K, U> mapView(Map<K, V> map, Function<? super V, ? extends U> valueMapper) {
88
        return new AbstractMap<K, U>() {
×
89
            @Override
90
            public U get(Object key) {
91
                V v = map.get(key);
×
92
                if (v == null && !map.containsKey(key)) {
×
93
                    return null;
×
94
                }
95
                return valueMapper.apply(v);
×
96
            }
97

98
            @Override
99
            public Set<Entry<K, U>> entrySet() {
100
                return new AbstractSet<Entry<K, U>>() {
×
101

102
                    @Override
103
                    public Iterator<Entry<K, U>> iterator() {
104
                        return IteratorUtil.map(map.entrySet().iterator(), entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), valueMapper.apply(entry.getValue())));
×
105
                    }
106

107
                    @Override
108
                    public int size() {
109
                        return map.size();
×
110
                    }
111
                };
112
            }
113
        };
114
    }
115

116

117
    /**
118
     * Returns the set union of the given collections.
119
     *
120
     * @param c1 First collection
121
     * @param c2 Second collection
122
     *
123
     * @return Union of both arguments
124
     */
125
    @SafeVarargs
126
    public static <T> Set<T> union(Collection<? extends T> c1, Collection<? extends T> c2, Collection<? extends T>... rest) {
127
        Set<T> union = new LinkedHashSet<>(c1);
×
128
        union.addAll(c2);
×
129
        for (Collection<? extends T> ts : rest) {
×
130
            union.addAll(ts);
×
131
        }
132
        return union;
×
133
    }
134

135
    /**
136
     * Returns the set intersection of the given collections.
137
     *
138
     * @param c1 First collection
139
     * @param c2 Second collection
140
     *
141
     * @return Intersection of both arguments
142
     */
143
    @SafeVarargs
144
    public static <T> Set<T> intersect(Collection<? extends T> c1, Collection<? extends T> c2, Collection<? extends T>... rest) {
145
        Set<T> union = new LinkedHashSet<>(c1);
×
146
        union.retainAll(c2);
×
147
        for (Collection<? extends T> ts : rest) {
×
148
            union.retainAll(ts);
×
149
        }
150
        return union;
×
151
    }
152

153

154
    /**
155
     * Returns the set difference of the first collection with the other
156
     * collections.
157
     *
158
     * @param c1 First collection
159
     * @param c2 Second collection
160
     *
161
     * @return Difference of arguments
162
     */
163
    @SafeVarargs
164
    public static <T> Set<T> diff(Collection<? extends T> c1, Collection<? extends T> c2, Collection<? extends T>... rest) {
165
        Set<T> union = new LinkedHashSet<>(c1);
×
166
        union.removeAll(c2);
×
167
        for (Collection<? extends T> ts : rest) {
×
168
            union.removeAll(ts);
×
169
        }
170
        return union;
×
171
    }
172

173
    /**
174
     * Returns a set containing the given elements. No guarantee is
175
     * made about mutability.
176
     *
177
     * @param first First element
178
     * @param rest  Following elements
179
     */
180
    @SafeVarargs
181
    public static <T> Set<T> setOf(T first, T... rest) {
182
        return immutableSetOf(first, rest);
1✔
183
    }
184

185
    /**
186
     * Returns an unmodifiable set containing the given elements.
187
     *
188
     * @param first First element
189
     * @param rest  Following elements
190
     */
191
    @SafeVarargs
192
    public static <T> Set<T> immutableSetOf(T first, T... rest) {
193
        if (rest.length == 0) {
1✔
194
            return Collections.singleton(first);
1✔
195
        }
196
        Set<T> union = new LinkedHashSet<>();
1✔
197
        union.add(first);
1✔
198
        Collections.addAll(union, rest);
1✔
199
        return Collections.unmodifiableSet(union);
1✔
200
    }
201

202
    /**
203
     * Returns an unmodifiable set containing the given elements.
204
     *
205
     * @param first First element
206
     * @param rest  Following elements
207
     */
208
    @SafeVarargs
209
    public static <T extends Enum<T>> Set<T> immutableEnumSet(T first, T... rest) {
210
        return Collections.unmodifiableSet(EnumSet.of(first, rest));
×
211
    }
212

213

214
    @SafeVarargs
215
    public static <T> List<T> listOf(T first, T... rest) {
216
        if (rest.length == 0) {
1✔
217
            return ConsPStack.singleton(first);
1✔
218
        }
219
        List<T> union = new ArrayList<>();
1✔
220
        union.add(first);
1✔
221
        union.addAll(asList(rest));
1✔
222
        return Collections.unmodifiableList(union);
1✔
223
    }
224

225
    public static <K, V> Map<K, V> mapOf(K k0, V v0) {
226
        return Collections.singletonMap(k0, v0);
1✔
227
    }
228

229
    public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2) {
230
        Map<K, V> map = new LinkedHashMap<>();
1✔
231
        map.put(k1, v1);
1✔
232
        map.put(k2, v2);
1✔
233
        return Collections.unmodifiableMap(map);
1✔
234
    }
235

236
    /**
237
     * @since 7.21.0.
238
     */
239
    public static <K, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3) {
NEW
240
        Map<K, V> map = new LinkedHashMap<>();
×
NEW
241
        map.put(k1, v1);
×
NEW
242
        map.put(k2, v2);
×
NEW
243
        map.put(k3, v3);
×
NEW
244
        return Collections.unmodifiableMap(map);
×
245
    }
246

247
    public static <K, V> Map<K, V> buildMap(Consumer<Map<K, V>> effect) {
248
        Map<K, V> map = new LinkedHashMap<>();
1✔
249
        effect.accept(map);
1✔
250
        return Collections.unmodifiableMap(map);
1✔
251
    }
252

253
    public static <K, V> Map<K, V> buildMap(Map<K, V> initialMap, Consumer<Map<K, V>> effect) {
254
        Map<K, V> map = new LinkedHashMap<>(initialMap);
1✔
255
        effect.accept(map);
1✔
256
        return Collections.unmodifiableMap(map);
1✔
257
    }
258

259
    public static <T, R> List<@NonNull R> mapNotNull(Iterable<? extends T> from, Function<? super T, ? extends @Nullable R> f) {
260
        Iterator<? extends T> it = from.iterator();
×
261
        if (!it.hasNext()) {
×
262
            return Collections.emptyList();
×
263
        }
264
        List<R> res = new ArrayList<>();
×
265
        while (it.hasNext()) {
×
266
            R r = f.apply(it.next());
×
267
            if (r != null) {
×
268
                res.add(r);
×
269
            }
270
        }
×
271
        return res;
×
272
    }
273

274
    /**
275
     * Produce a new map with the mappings of the first, and one additional
276
     * mapping. The returned map may be unmodifiable.
277
     */
278
    public static <K, V> Map<K, V> plus(Map<K, V> m, K k, V v) {
279
        AssertionUtil.requireParamNotNull("map", m);
×
280
        if (m instanceof PMap) {
×
281
            return ((PMap<K, V>) m).plus(k, v);
×
282
        }
283
        if (m.isEmpty()) {
×
284
            return Collections.singletonMap(k, v);
×
285
        }
286
        Map<K, V> newM = new HashMap<>(m);
×
287
        newM.put(k, v);
×
288
        return newM;
×
289
    }
290

291
    /**
292
     * Produce a new list with the elements of the first, and one additional
293
     * item. The returned list is immutable.
294
     */
295
    public static <V> List<V> plus(List<V> list, V v) {
296
        if (list instanceof PSequence) {
×
297
            return ((PSequence<V>) list).plus(v);
×
298
        } else if (list.isEmpty()) {
×
299
            return ConsPStack.singleton(v);
×
300
        }
301
        return ConsPStack.from(list).plus(v);
×
302
    }
303

304
    /** Returns the empty list. */
305
    public static <V> List<V> emptyList() {
306
        // We use this implementation so that it plays well with other
307
        // operations that expect immutable data.
308
        return ConsPStack.empty();
1✔
309
    }
310

311
    /**
312
     * Returns an unmodifiable set containing the set union of the collection,
313
     * and the new elements.
314
     */
315
    @SafeVarargs
316
    @SuppressWarnings("unchecked")
317
    public static <V> Set<V> setUnion(Collection<? extends V> set, V first, V... newElements) {
318
        if (set instanceof PSet) {
1!
319
            return ((PSet<V>) set).plus(first).plusAll(asList(newElements));
×
320
        }
321
        Set<V> newSet = new LinkedHashSet<>(set.size() + 1 + newElements.length);
1✔
322
        newSet.addAll(set);
1✔
323
        newSet.add(first);
1✔
324
        Collections.addAll(newSet, newElements);
1✔
325
        return Collections.unmodifiableSet(newSet);
1✔
326
    }
327

328
    /**
329
     * Returns the key that corresponds to the given value in the map,
330
     * or null if it is not contained in the map.
331
     *
332
     * @param m   Map
333
     * @param v   Value
334
     * @param <K> Type of keys
335
     * @param <V> Type of values
336
     *
337
     * @throws NullPointerException If the entry is found, but the key
338
     *                              is null
339
     * @throws NullPointerException If the map is null
340
     */
341
    public static <@NonNull K, V> @Nullable K getKeyOfValue(Map<K, V> m, V v) {
342
        AssertionUtil.requireParamNotNull("map", m);
×
343
        for (Entry<K, V> it : m.entrySet()) {
×
344
            if (it.getValue().equals(v)) {
×
345
                return Objects.requireNonNull(it.getKey(), "This method uses null as a sentinel value");
×
346
            }
347
        }
×
348
        return null;
×
349
    }
350

351

352
    /**
353
     * Returns a map associating each key in the first list to its
354
     * corresponding value in the second.
355
     *
356
     * @throws IllegalArgumentException If the list size are mismatched
357
     * @throws NullPointerException     If either of the parameter is null,
358
     *                                  or any of the keys or values are null
359
     */
360
    public static <K, V> Map<K, V> zip(List<? extends @NonNull K> from, List<? extends @NonNull V> to) {
361
        AssertionUtil.requireParamNotNull("keys", from);
×
362
        AssertionUtil.requireParamNotNull("values", to);
×
363
        Validate.isTrue(from.size() == to.size(), "Mismatched list sizes %s to %s", from, to);
×
364

365
        if (from.isEmpty()) {
×
366
            return emptyMap();
×
367
        }
368

369
        Map<K, V> map = new HashMap<>(from.size());
×
370

371
        for (int i = 0; i < from.size(); i++) {
×
372
            K key = from.get(i);
×
373
            V val = to.get(i);
×
374

375
            Validate.notNull(key);
×
376
            Validate.notNull(val);
×
377

378
            map.put(key, val);
×
379
        }
380

381
        return map;
×
382
    }
383

384
    public static <K, V> Map<K, V> associateWith(Collection<? extends @NonNull K> keys, Function<? super K, ? extends V> mapper) {
385
        AssertionUtil.requireParamNotNull("keys", keys);
×
386
        if (keys.isEmpty()) {
×
387
            return emptyMap();
×
388
        }
389

390
        return associateWithTo(new HashMap<>(keys.size()), keys, mapper);
×
391
    }
392

393
    public static <K, V> Map<K, V> associateWithTo(Map<K, V> collector, Collection<? extends @NonNull K> keys, Function<? super K, ? extends V> mapper) {
394
        AssertionUtil.requireParamNotNull("collector", collector);
×
395
        AssertionUtil.requireParamNotNull("keys", keys);
×
396
        AssertionUtil.requireParamNotNull("mapper", mapper);
×
397
        for (K key : keys) {
×
398
            collector.put(key, mapper.apply(key));
×
399
        }
×
400
        return collector;
×
401
    }
402

403

404
    public static <K, V> Map<K, V> associateBy(Collection<? extends @NonNull V> values, Function<? super V, ? extends K> keyMapper) {
405
        AssertionUtil.requireParamNotNull("values", values);
1✔
406
        if (values.isEmpty()) {
1✔
407
            return emptyMap();
1✔
408
        }
409

410
        return associateByTo(new HashMap<>(values.size()), values, keyMapper);
1✔
411
    }
412

413

414
    public static <K, V> Map<K, V> associateByTo(Map<K, V> collector, Collection<? extends @NonNull V> values, Function<? super V, ? extends K> keyMapper) {
415
        AssertionUtil.requireParamNotNull("collector", collector);
1✔
416
        AssertionUtil.requireParamNotNull("values", values);
1✔
417
        AssertionUtil.requireParamNotNull("keyMapper", keyMapper);
1✔
418
        for (V v : values) {
1✔
419
            collector.put(keyMapper.apply(v), v);
1✔
420
        }
1✔
421
        return collector;
1✔
422
    }
423

424
    /**
425
     * Map each element of the given collection with the given function,
426
     * and accumulates it into an unmodifiable list.
427
     */
428
    public static <T, R> List<R> map(Collection<? extends T> from, Function<? super T, ? extends R> f) {
429
        if (from == null) {
1!
430
            return emptyList();
×
431
        }
432
        return map(from.iterator(), from.size(), f);
1✔
433
    }
434

435
    /**
436
     * Map each element of the given iterable with the given function,
437
     * and accumulates it into an unmodifiable list.
438
     */
439
    public static <T, R> List<R> map(Iterable<? extends T> from, Function<? super T, ? extends R> f) {
440
        if (from == null) {
1!
441
            return emptyList();
×
442
        }
443
        return map(from.iterator(), UNKNOWN_SIZE, f);
1✔
444
    }
445

446
    /**
447
     * Map each element of the given array with the given function,
448
     * and accumulates it into an unmodifiable list.
449
     */
450
    public static <T, R> List<R> map(T[] from, Function<? super T, ? extends R> f) {
451
        if (from == null) {
1!
452
            return emptyList();
×
453
        }
454
        return map(asList(from), f);
1✔
455
    }
456

457
    /**
458
     * Map each element of the given iterator with the given function,
459
     * and accumulates it into an unmodifiable list.
460
     */
461
    public static <T, R> List<R> map(Iterator<? extends T> from, Function<? super T, ? extends R> f) {
462
        if (from == null) {
×
463
            return emptyList();
×
464
        }
465
        return map(from, UNKNOWN_SIZE, f);
×
466
    }
467

468
    private static <T, R> List<R> map(Iterator<? extends T> from, int sizeHint, Function<? super T, ? extends R> f) {
469
        if (!from.hasNext()) {
1✔
470
            return emptyList();
1✔
471
        } else if (sizeHint == 1) {
1✔
472
            return ConsPStack.singleton(f.apply(from.next()));
1✔
473
        }
474
        List<R> res = sizeHint == UNKNOWN_SIZE ? new ArrayList<>() : new ArrayList<>(sizeHint);
1✔
475
        while (from.hasNext()) {
1✔
476
            res.add(f.apply(from.next()));
1✔
477
        }
478
        return Collections.unmodifiableList(res);
1✔
479
    }
480

481
    /**
482
     * Map each element of the given iterable with the given function,
483
     * and accumulates it into the collector.
484
     */
485
    public static <T, U, A, C> C map(Collector<? super U, A, ? extends C> collector,
486
                                     Iterable<? extends T> from,
487
                                     Function<? super T, ? extends U> f) {
488
        if (from == null) {
1!
489
            return map(collector, emptyIterator(), f);
×
490
        }
491
        return map(collector, from.iterator(), f);
1✔
492
    }
493

494
    /**
495
     * Map each element of the given iterator with the given function,
496
     * and accumulates it into the collector.
497
     */
498
    // one more type param and we can write tupac
499
    public static <T, U, A, C> C map(Collector<? super U, A, ? extends C> collector,
500
                                     Iterator<? extends T> from,
501
                                     Function<? super T, ? extends U> f) {
502
        A a = collector.supplier().get();
1✔
503
        BiConsumer<A, ? super U> accumulator = collector.accumulator();
1✔
504
        from.forEachRemaining(t -> accumulator.accept(a, f.apply(t)));
1✔
505
        return finish(collector, a);
1✔
506
    }
507

508
    /**
509
     * A collector that returns a mutable list. This contrasts with
510
     * {@link Collectors#toList()}, which makes no guarantee about the
511
     * mutability of the list.
512
     *
513
     * @param <T> Type of accumulated values
514
     */
515
    public static <T> Collector<T, ?, List<T>> toMutableList() {
516
        return Collectors.toCollection(ArrayList::new);
1✔
517
    }
518

519

520
    /**
521
     * A collector that returns a mutable set. This contrasts with
522
     * {@link Collectors#toSet()}, which makes no guarantee about the
523
     * mutability of the set. The set preserves insertion order.
524
     *
525
     * @param <T> Type of accumulated values
526
     */
527
    public static <T> Collector<T, ?, Set<T>> toMutableSet() {
528
        return Collectors.toCollection(LinkedHashSet::new);
1✔
529
    }
530

531
    /**
532
     * A collector that returns an unmodifiable list. This contrasts with
533
     * {@link Collectors#toList()}, which makes no guarantee about the
534
     * mutability of the list. {@code Collectors::toUnmodifiableList} was
535
     * only added in JDK 9.
536
     *
537
     * @param <T> Type of accumulated values
538
     */
539
    public static <T> Collector<T, ?, List<T>> toUnmodifiableList() {
540
        return Collectors.collectingAndThen(toMutableList(), Collections::unmodifiableList);
1✔
541
    }
542

543
    /**
544
     * A collector that returns an unmodifiable set. This contrasts with
545
     * {@link Collectors#toSet()}, which makes no guarantee about the
546
     * mutability of the set. {@code Collectors::toUnmodifiableSet} was
547
     * only added in JDK 9. The set preserves insertion order.
548
     *
549
     * @param <T> Type of accumulated values
550
     */
551
    public static <T> Collector<T, ?, Set<T>> toUnmodifiableSet() {
552
        return Collectors.collectingAndThen(toMutableSet(), Collections::unmodifiableSet);
1✔
553
    }
554

555
    /**
556
     * A collectors that accumulates into a persistent set.
557
     *
558
     * @param <T> Type of accumulated values
559
     */
560
    public static <T> Collector<T, ?, PSet<T>> toPersistentSet() {
561
        class Holder {
×
562

563
            PSet<T> set = HashTreePSet.empty();
×
564
        }
565

566
        return Collector.of(
×
567
            Holder::new,
×
568
            (h, t) -> {
569
                h.set = h.set.plus(t);
×
570
            },
×
571
            (left, right) -> {
572
                left.set = left.set.plusAll(right.set);
×
573
                return left;
×
574
            },
575
            a -> a.set
×
576
        );
577
    }
578

579
    /**
580
     * Finish the accumulated value of the collector.
581
     */
582
    public static <V, A, C> C finish(Collector<? super V, A, ? extends C> collector, A acc) {
583
        if (collector.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
1!
584
            return (C) acc;
1✔
585
        } else {
586
            return collector.finisher().apply(acc);
×
587
        }
588
    }
589

590
    public static <T> List<T> drop(List<T> list, int n) {
591
        AssertionUtil.requireNonNegative("n", n);
1✔
592

593
        return list.size() <= n ? emptyList()
1✔
594
                                : list.subList(n, list.size());
1✔
595
    }
596

597
    public static <T> List<T> take(List<T> list, int n) {
598
        AssertionUtil.requireNonNegative("n", n);
1✔
599
        return list.size() <= n ? list
1!
600
                                : list.subList(0, n);
1✔
601
    }
602

603

604
    public static <T> List<T> listOfNotNull(T t) {
605
        return t == null ? emptyList() : ConsPStack.singleton(t);
×
606
    }
607

608
    /**
609
     * Returns true if any element of the iterable matches the predicate. Return
610
     * false if the list is null or empty.
611
     */
612
    public static <N> boolean any(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
613
        return list != null && IteratorUtil.anyMatch(list.iterator(), predicate);
1✔
614
    }
615

616
    /**
617
     * Returns true if all elements of the iterable match the predicate. Return
618
     * true if the list is null or empty.
619
     */
620
    public static <N> boolean all(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
621
        return list == null || IteratorUtil.allMatch(list.iterator(), predicate);
×
622
    }
623

624
    /**
625
     * Returns true if no element of the iterable matches the predicate. Return
626
     * true if the list is null or empty.
627
     */
628
    public static <N> boolean none(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
629
        return list == null || IteratorUtil.noneMatch(list.iterator(), predicate);
×
630
    }
631

632
    /**
633
     * If the set has a single element, returns it, otherwise returns null.
634
     * Obviously the set should not contain null elements.
635
     */
636
    public static <@NonNull T> @Nullable T asSingle(Set<T> set) {
637
        if (set.size() == 1) {
×
638
            return set.iterator().next();
×
639
        } else {
640
            return null;
×
641
        }
642
    }
643

644
    /**
645
     * Returns an unmodifiable copy of the list. This is to be preferred
646
     * to {@link Collections#unmodifiableList(List)} if you don't trust
647
     * the source of the list, because no one holds a reference to the buffer
648
     * except the returned unmodifiable list.
649
     *
650
     * @param list A list
651
     * @param <T>  Type of items
652
     */
653
    public static <T> List<T> defensiveUnmodifiableCopy(List<? extends T> list) {
654
        if (list instanceof PSequence) {
×
655
            return (List<T>) list; // is already immutable
×
656
        }
657
        if (list.isEmpty()) {
×
658
            return ConsPStack.empty();
×
659
        } else if (list.size() == 1) {
×
660
            return ConsPStack.singleton(list.get(0));
×
661
        }
662
        return ConsPStack.from(list);
×
663
    }
664

665
    public static <T> Set<T> defensiveUnmodifiableCopyToSet(Collection<? extends T> list) {
666
        if (list.isEmpty()) {
×
667
            return emptySet();
×
668
        }
669
        return Collections.unmodifiableSet(new LinkedHashSet<>(list));
×
670
    }
671

672
    /**
673
     * Like {@link String#join(CharSequence, Iterable)}, except it appends
674
     * on a preexisting {@link StringBuilder}. The result value is that StringBuilder.
675
     */
676
    public static <T> StringBuilder joinOn(StringBuilder sb,
677
                                           Iterable<? extends T> iterable,
678
                                           BiConsumer<? super StringBuilder, ? super T> appendItem,
679
                                           String delimiter) {
680
        boolean first = true;
1✔
681
        for (T t : iterable) {
1✔
682
            if (first) {
1✔
683
                first = false;
1✔
684
            } else {
685
                sb.append(delimiter);
1✔
686
            }
687
            appendItem.accept(sb, t);
1✔
688
        }
1✔
689
        return sb;
1✔
690
    }
691

692
    public static @NonNull StringBuilder joinCharsIntoStringBuilder(List<Chars> lines, String delimiter) {
693
        return joinOn(
1✔
694
            new StringBuilder(),
695
            lines,
696
            (buf, line) -> line.appendChars(buf),
1✔
697
            delimiter
698
        );
699
    }
700

701

702
    /**
703
     * Merge the second map into the first. If some keys are in common,
704
     * merge them using the merge function, like {@link Map#merge(Object, Object, BiFunction)}.
705
     */
706
    public static <K, V> void mergeMaps(Map<K, V> result, Map<K, V> other, BinaryOperator<V> mergeFun) {
707
        for (K otherKey : other.keySet()) {
×
708
            V otherInfo = other.get(otherKey); // non-null
×
709
            result.merge(otherKey, otherInfo, mergeFun);
×
710
        }
×
711
    }
×
712

713
    /**
714
     * Union of two PSets, which avoids creating a new pset if possible.
715
     */
716
    public static <V> PSet<V> union(PSet<V> as, PSet<V> bs) {
717
        if (as.isEmpty()) {
×
718
            return bs;
×
719
        } else if (bs.isEmpty()) {
×
720
            return as;
×
721
        }
722
        return as.plusAll(bs);
×
723
    }
724

725
    public static <T> @NonNull List<T> makeUnmodifiableAndNonNull(@Nullable List<? extends T> list) {
726
        if (list instanceof PSequence) {
×
727
            return (List<T>) list;
×
728
        }
729
        return list == null || list.isEmpty() ? emptyList()
×
730
                                              : Collections.unmodifiableList(list);
×
731
    }
732
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc