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

pmd / pmd / #3722

pending completion
#3722

push

github actions

adangel
Suppress MissingOverride for Chars::isEmpty (#4291)

67270 of 127658 relevant lines covered (52.7%)

0.53 hits per line

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

46.92
/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.ArrayList;
13
import java.util.Collection;
14
import java.util.Collections;
15
import java.util.EnumSet;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.Iterator;
19
import java.util.LinkedHashMap;
20
import java.util.LinkedHashSet;
21
import java.util.List;
22
import java.util.Map;
23
import java.util.Set;
24
import java.util.function.BiConsumer;
25
import java.util.function.BiFunction;
26
import java.util.function.BinaryOperator;
27
import java.util.function.Consumer;
28
import java.util.function.Function;
29
import java.util.function.Predicate;
30
import java.util.stream.Collector;
31
import java.util.stream.Collector.Characteristics;
32
import java.util.stream.Collectors;
33

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

43
import net.sourceforge.pmd.annotation.InternalApi;
44
import net.sourceforge.pmd.internal.util.AssertionUtil;
45
import net.sourceforge.pmd.internal.util.IteratorUtil;
46
import net.sourceforge.pmd.lang.document.Chars;
47

48
/**
49
 * Generic collection and array-related utility functions for java.util types.
50
 * See ClassUtil for comparable facilities for short name lookup.
51
 *
52
 * @author Brian Remedios
53
 * @version $Revision$
54
 * @deprecated Is internal API
55
 */
56
@Deprecated
57
@InternalApi
58
public final class CollectionUtil {
59

60
    private static final int UNKNOWN_SIZE = -1;
61

62
    @SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
63
    public static final Set<String> COLLECTION_INTERFACES_BY_NAMES = collectionTypes(List.class, Collection.class, Map.class, Set.class);
1✔
64

65
    @SuppressWarnings({"PMD.LooseCoupling", "PMD.UnnecessaryFullyQualifiedName"})
66
    public static final Set<String> COLLECTION_CLASSES_BY_NAMES
1✔
67
        = collectionTypes(ArrayList.class, java.util.LinkedList.class, java.util.Vector.class, HashMap.class,
1✔
68
                          java.util.LinkedHashMap.class, java.util.TreeMap.class, java.util.TreeSet.class,
69
                          HashSet.class, java.util.LinkedHashSet.class, java.util.Hashtable.class);
70

71

72
    private CollectionUtil() {
73
    }
74

75
    private static Set<String> collectionTypes(Class<?>... types) {
76
        Set<String> set = new HashSet<>();
1✔
77

78
        for (Class<?> type : types) {
1✔
79
            if (!set.add(type.getSimpleName()) || !set.add(type.getName())) {
1✔
80
                throw new IllegalArgumentException("Duplicate or name collision for " + type);
×
81
            }
82
        }
83

84
        return set;
1✔
85
    }
86

87
    /**
88
     * Return whether we can identify the typeName as a java.util collection
89
     * class or interface as specified.
90
     *
91
     * @param typeName
92
     *            String
93
     * @param includeInterfaces
94
     *            boolean
95
     * @return boolean
96
     *
97
     * @deprecated Will be replaced with type resolution
98
     */
99
    @Deprecated
100
    public static boolean isCollectionType(String typeName, boolean includeInterfaces) {
101
        return COLLECTION_CLASSES_BY_NAMES.contains(typeName)
×
102
                || includeInterfaces && COLLECTION_INTERFACES_BY_NAMES.contains(typeName);
×
103
    }
104

105
    /**
106
     * Creates and returns a map populated with the keyValuesSets where the
107
     * value held by the tuples are they key and value in that order.
108
     *
109
     * @param keys
110
     *            K[]
111
     * @param values
112
     *            V[]
113
     * @return Map
114
     *
115
     * @deprecated Used by deprecated property types
116
     */
117
    @Deprecated
118
    public static <K, V> Map<K, V> mapFrom(K[] keys, V[] values) {
119
        if (keys.length != values.length) {
1✔
120
            throw new RuntimeException("mapFrom keys and values arrays have different sizes");
1✔
121
        }
122
        Map<K, V> map = new HashMap<>(keys.length);
1✔
123
        for (int i = 0; i < keys.length; i++) {
1✔
124
            map.put(keys[i], values[i]);
1✔
125
        }
126
        return map;
1✔
127
    }
128

129
    /**
130
     * Returns a map based on the source but with the key &amp; values swapped.
131
     *
132
     * @param source
133
     *            Map
134
     * @return Map
135
     *
136
     * @deprecated Used by deprecated property types
137
     */
138
    @Deprecated
139
    public static <K, V> Map<V, K> invertedMapFrom(Map<K, V> source) {
140
        Map<V, K> map = new HashMap<>(source.size());
1✔
141
        for (Map.Entry<K, V> entry : source.entrySet()) {
1✔
142
            map.put(entry.getValue(), entry.getKey());
1✔
143
        }
1✔
144
        return map;
1✔
145
    }
146

147

148
    /**
149
     * Returns a list view that pretends it is the concatenation of
150
     * both lists. The returned view is unmodifiable. The implementation
151
     * is pretty stupid and not optimized for repeated concatenation,
152
     * but should be ok for smallish chains of random-access lists.
153
     *
154
     * @param head Head elements (to the left)
155
     * @param tail Tail elements (to the right)
156
     * @param <T>  Type of elements in both lists
157
     *
158
     * @return A concatenated view
159
     */
160
    public static <T> List<T> concatView(List<? extends T> head, List<? extends T> tail) {
161
        if (head.isEmpty()) {
×
162
            return makeUnmodifiableAndNonNull(tail);
×
163
        } else if (tail.isEmpty()) {
×
164
            return makeUnmodifiableAndNonNull(head);
×
165
        } else {
166
            return new ConsList<>(head, tail);
×
167
        }
168
    }
169

170

171
    /**
172
     * Returns the set union of the given collections.
173
     *
174
     * @param c1 First collection
175
     * @param c2 Second collection
176
     *
177
     * @return Union of both arguments
178
     */
179
    @SafeVarargs
180
    public static <T> Set<T> union(Collection<? extends T> c1, Collection<? extends T> c2, Collection<? extends T>... rest) {
181
        Set<T> union = new LinkedHashSet<>(c1);
×
182
        union.addAll(c2);
×
183
        for (Collection<? extends T> ts : rest) {
×
184
            union.addAll(ts);
×
185
        }
186
        return union;
×
187
    }
188

189
    /**
190
     * Returns the set intersection of the given collections.
191
     *
192
     * @param c1 First collection
193
     * @param c2 Second collection
194
     *
195
     * @return Intersection of both arguments
196
     */
197
    @SafeVarargs
198
    public static <T> Set<T> intersect(Collection<? extends T> c1, Collection<? extends T> c2, Collection<? extends T>... rest) {
199
        Set<T> union = new LinkedHashSet<>(c1);
×
200
        union.retainAll(c2);
×
201
        for (Collection<? extends T> ts : rest) {
×
202
            union.retainAll(ts);
×
203
        }
204
        return union;
×
205
    }
206

207

208
    /**
209
     * Returns the set difference of the first collection with the other
210
     * collections.
211
     *
212
     * @param c1 First collection
213
     * @param c2 Second collection
214
     *
215
     * @return Difference of arguments
216
     */
217
    @SafeVarargs
218
    public static <T> Set<T> diff(Collection<? extends T> c1, Collection<? extends T> c2, Collection<? extends T>... rest) {
219
        Set<T> union = new LinkedHashSet<>(c1);
×
220
        union.removeAll(c2);
×
221
        for (Collection<? extends T> ts : rest) {
×
222
            union.removeAll(ts);
×
223
        }
224
        return union;
×
225
    }
226

227
    /**
228
     * Returns a set containing the given elements. No guarantee is
229
     * made about mutability.
230
     *
231
     * @param first First element
232
     * @param rest  Following elements
233
     */
234
    @SafeVarargs
235
    public static <T> Set<T> setOf(T first, T... rest) {
236
        return immutableSetOf(first, rest);
1✔
237
    }
238

239
    /**
240
     * Returns an unmodifiable set containing the given elements.
241
     *
242
     * @param first First element
243
     * @param rest  Following elements
244
     */
245
    @SafeVarargs
246
    public static <T> Set<T> immutableSetOf(T first, T... rest) {
247
        if (rest.length == 0) {
1✔
248
            return Collections.singleton(first);
1✔
249
        }
250
        Set<T> union = new LinkedHashSet<>();
1✔
251
        union.add(first);
1✔
252
        Collections.addAll(union, rest);
1✔
253
        return Collections.unmodifiableSet(union);
1✔
254
    }
255

256
    /**
257
     * Returns an unmodifiable set containing the given elements.
258
     *
259
     * @param first First element
260
     * @param rest  Following elements
261
     */
262
    @SafeVarargs
263
    public static <T extends Enum<T>> Set<T> immutableEnumSet(T first, T... rest) {
264
        return Collections.unmodifiableSet(EnumSet.of(first, rest));
×
265
    }
266

267

268
    @SafeVarargs
269
    public static <T> List<T> listOf(T first, T... rest) {
270
        if (rest.length == 0) {
1✔
271
            return ConsPStack.singleton(first);
1✔
272
        }
273
        List<T> union = new ArrayList<>();
1✔
274
        union.add(first);
1✔
275
        union.addAll(asList(rest));
1✔
276
        return Collections.unmodifiableList(union);
1✔
277
    }
278

279
    public static <K, V> Map<K, V> mapOf(K k0, V v0) {
280
        return Collections.singletonMap(k0, v0);
×
281
    }
282

283
    public static <K, V> Map<K, V> buildMap(Consumer<Map<K, V>> effect) {
284
        Map<K, V> map = new LinkedHashMap<>();
1✔
285
        effect.accept(map);
1✔
286
        return Collections.unmodifiableMap(map);
1✔
287
    }
288

289
    public static <K, V> Map<K, V> buildMap(Map<K, V> initialMap, Consumer<Map<K, V>> effect) {
290
        Map<K, V> map = new LinkedHashMap<>(initialMap);
1✔
291
        effect.accept(map);
1✔
292
        return Collections.unmodifiableMap(map);
1✔
293
    }
294

295
    public static <T, R> List<@NonNull R> mapNotNull(Iterable<? extends T> from, Function<? super T, ? extends @Nullable R> f) {
296
        Iterator<? extends T> it = from.iterator();
×
297
        if (!it.hasNext()) {
×
298
            return Collections.emptyList();
×
299
        }
300
        List<R> res = new ArrayList<>();
×
301
        while (it.hasNext()) {
×
302
            R r = f.apply(it.next());
×
303
            if (r != null) {
×
304
                res.add(r);
×
305
            }
306
        }
×
307
        return res;
×
308
    }
309

310
    /**
311
     * Produce a new map with the mappings of the first, and one additional
312
     * mapping. The returned map may be unmodifiable.
313
     */
314
    public static <K, V> Map<K, V> plus(Map<K, V> m, K k, V v) {
315
        if (m instanceof PMap) {
×
316
            return ((PMap<K, V>) m).plus(k, v);
×
317
        }
318
        if (m.isEmpty()) {
×
319
            return Collections.singletonMap(k, v);
×
320
        }
321
        Map<K, V> newM = new HashMap<>(m);
×
322
        newM.put(k, v);
×
323
        return newM;
×
324
    }
325

326
    /**
327
     * Produce a new list with the elements of the first, and one additional
328
     * item. The returned list is immutable.
329
     */
330
    public static <V> List<V> plus(List<V> list, V v) {
331
        if (list instanceof PSequence) {
×
332
            return ((PSequence<V>) list).plus(v);
×
333
        } else if (list.isEmpty()) {
×
334
            return ConsPStack.singleton(v);
×
335
        }
336
        return ConsPStack.from(list).plus(v);
×
337
    }
338

339
    /** Returns the empty list. */
340
    public static <V> List<V> emptyList() {
341
        // We use this implementation so that it plays well with other
342
        // operations that expect immutable data.
343
        return ConsPStack.empty();
1✔
344
    }
345

346
    /**
347
     * Returns an unmodifiable set containing the set union of the collection,
348
     * and the new elements.
349
     */
350
    @SafeVarargs
351
    @SuppressWarnings("unchecked")
352
    public static <V> Set<V> setUnion(Collection<? extends V> set, V first, V... newElements) {
353
        if (set instanceof PSet) {
1✔
354
            return ((PSet<V>) set).plus(first).plusAll(asList(newElements));
×
355
        }
356
        Set<V> newSet = new LinkedHashSet<>(set.size() + 1 + newElements.length);
1✔
357
        newSet.addAll(set);
1✔
358
        newSet.add(first);
1✔
359
        Collections.addAll(newSet, newElements);
1✔
360
        return Collections.unmodifiableSet(newSet);
1✔
361
    }
362

363

364
    /**
365
     * Returns a map associating each key in the first list to its
366
     * corresponding value in the second.
367
     *
368
     * @throws IllegalArgumentException If the list size are mismatched
369
     * @throws NullPointerException     If either of the parameter is null,
370
     *                                  or any of the keys or values are null
371
     */
372
    public static <K, V> Map<K, V> zip(List<? extends @NonNull K> from, List<? extends @NonNull V> to) {
373
        AssertionUtil.requireParamNotNull("keys", from);
×
374
        AssertionUtil.requireParamNotNull("values", to);
×
375
        Validate.isTrue(from.size() == to.size(), "Mismatched list sizes %s to %s", from, to);
×
376

377
        if (from.isEmpty()) { //NOPMD: we really want to compare references here
×
378
            return emptyMap();
×
379
        }
380

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

383
        for (int i = 0; i < from.size(); i++) {
×
384
            K key = from.get(i);
×
385
            V val = to.get(i);
×
386

387
            Validate.notNull(key);
×
388
            Validate.notNull(val);
×
389

390
            map.put(key, val);
×
391
        }
392

393
        return map;
×
394
    }
395

396
    public static <K, V> Map<K, V> associateWith(Collection<? extends @NonNull K> keys, Function<? super K, ? extends V> mapper) {
397
        AssertionUtil.requireParamNotNull("keys", keys);
×
398
        if (keys.isEmpty()) {
×
399
            return emptyMap();
×
400
        }
401

402
        return associateWithTo(new HashMap<>(keys.size()), keys, mapper);
×
403
    }
404

405
    public static <K, V> Map<K, V> associateWithTo(Map<K, V> collector, Collection<? extends @NonNull K> keys, Function<? super K, ? extends V> mapper) {
406
        AssertionUtil.requireParamNotNull("collector", collector);
×
407
        AssertionUtil.requireParamNotNull("keys", keys);
×
408
        AssertionUtil.requireParamNotNull("mapper", mapper);
×
409
        for (K key : keys) {
×
410
            collector.put(key, mapper.apply(key));
×
411
        }
×
412
        return collector;
×
413
    }
414

415

416
    public static <K, V> Map<K, V> associateBy(Collection<? extends @NonNull V> values, Function<? super V, ? extends K> keyMapper) {
417
        AssertionUtil.requireParamNotNull("values", values);
1✔
418
        if (values.isEmpty()) {
1✔
419
            return emptyMap();
×
420
        }
421

422
        return associateByTo(new HashMap<>(values.size()), values, keyMapper);
1✔
423
    }
424

425

426
    public static <K, V> Map<K, V> associateByTo(Map<K, V> collector, Collection<? extends @NonNull V> values, Function<? super V, ? extends K> keyMapper) {
427
        AssertionUtil.requireParamNotNull("collector", collector);
1✔
428
        AssertionUtil.requireParamNotNull("values", values);
1✔
429
        AssertionUtil.requireParamNotNull("keyMapper", keyMapper);
1✔
430
        for (V v : values) {
1✔
431
            collector.put(keyMapper.apply(v), v);
1✔
432
        }
1✔
433
        return collector;
1✔
434
    }
435

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

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

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

469
    /**
470
     * Map each element of the given iterator with the given function,
471
     * and accumulates it into an unmodifiable list.
472
     */
473
    public static <T, R> List<R> map(Iterator<? extends T> from, Function<? super T, ? extends R> f) {
474
        if (from == null) {
×
475
            return emptyList();
×
476
        }
477
        return map(from, UNKNOWN_SIZE, f);
×
478
    }
479

480
    private static <T, R> List<R> map(Iterator<? extends T> from, int sizeHint, Function<? super T, ? extends R> f) {
481
        if (!from.hasNext()) {
1✔
482
            return emptyList();
1✔
483
        } else if (sizeHint == 1) {
1✔
484
            return ConsPStack.singleton(f.apply(from.next()));
1✔
485
        }
486
        List<R> res = sizeHint == UNKNOWN_SIZE ? new ArrayList<>() : new ArrayList<>(sizeHint);
1✔
487
        while (from.hasNext()) {
1✔
488
            res.add(f.apply(from.next()));
1✔
489
        }
490
        return Collections.unmodifiableList(res);
1✔
491
    }
492

493
    /**
494
     * Map each element of the given iterable with the given function,
495
     * and accumulates it into the collector.
496
     */
497
    public static <T, U, A, C> C map(Collector<? super U, A, ? extends C> collector,
498
                                     Iterable<? extends T> from,
499
                                     Function<? super T, ? extends U> f) {
500
        if (from == null) {
1✔
501
            return map(collector, emptyIterator(), f);
×
502
        }
503
        return map(collector, from.iterator(), f);
1✔
504
    }
505

506
    /**
507
     * Map each element of the given iterator with the given function,
508
     * and accumulates it into the collector.
509
     */
510
    // one more type param and we can write tupac
511
    public static <T, U, A, C> C map(Collector<? super U, A, ? extends C> collector,
512
                                     Iterator<? extends T> from,
513
                                     Function<? super T, ? extends U> f) {
514
        A a = collector.supplier().get();
1✔
515
        BiConsumer<A, ? super U> accumulator = collector.accumulator();
1✔
516
        from.forEachRemaining(t -> accumulator.accept(a, f.apply(t)));
1✔
517
        return finish(collector, a);
1✔
518
    }
519

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

531

532
    /**
533
     * A collector that returns a mutable set. This contrasts with
534
     * {@link Collectors#toSet()}, which makes no guarantee about the
535
     * mutability of the set. The set preserves insertion order.
536
     *
537
     * @param <T> Type of accumulated values
538
     */
539
    public static <T> Collector<T, ?, Set<T>> toMutableSet() {
540
        return Collectors.toCollection(LinkedHashSet::new);
1✔
541
    }
542

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

555
    /**
556
     * A collector that returns an unmodifiable set. This contrasts with
557
     * {@link Collectors#toSet()}, which makes no guarantee about the
558
     * mutability of the set. {@code Collectors::toUnmodifiableSet} was
559
     * only added in JDK 9. The set preserves insertion order.
560
     *
561
     * @param <T> Type of accumulated values
562
     */
563
    public static <T> Collector<T, ?, Set<T>> toUnmodifiableSet() {
564
        return Collectors.collectingAndThen(toMutableSet(), Collections::unmodifiableSet);
1✔
565
    }
566

567
    /**
568
     * A collectors that accumulates into a persistent set.
569
     *
570
     * @param <T> Type of accumulated values
571
     */
572
    public static <T> Collector<T, ?, PSet<T>> toPersistentSet() {
573
        class Holder {
×
574

575
            PSet<T> set = HashTreePSet.empty();
×
576
        }
577

578
        return Collector.of(
×
579
            Holder::new,
×
580
            (h, t) -> h.set = h.set.plus(t),
×
581
            (left, right) -> {
582
                left.set = left.set.plusAll(right.set);
×
583
                return left;
×
584
            },
585
            a -> a.set
×
586
        );
587
    }
588

589
    /**
590
     * Finish the accumulated value of the collector.
591
     */
592
    public static <V, A, C> C finish(Collector<? super V, A, ? extends C> collector, A acc) {
593
        if (collector.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
1✔
594
            return (C) acc;
1✔
595
        } else {
596
            return collector.finisher().apply(acc);
×
597
        }
598
    }
599

600
    public static <T> List<T> drop(List<T> list, int n) {
601
        AssertionUtil.requireNonNegative("n", n);
1✔
602

603
        return list.size() <= n ? emptyList()
1✔
604
                                : list.subList(n, list.size());
1✔
605
    }
606

607
    public static <T> List<T> take(List<T> list, int n) {
608
        AssertionUtil.requireNonNegative("n", n);
1✔
609
        return list.size() <= n ? list
1✔
610
                                : list.subList(0, n);
1✔
611
    }
612

613

614
    public static <T> List<T> listOfNotNull(T t) {
615
        return t == null ? emptyList() : ConsPStack.singleton(t);
×
616
    }
617

618
    /**
619
     * Returns true if any element of the iterable matches the predicate. Return
620
     * false if the list is null or empty.
621
     */
622
    public static <N> boolean any(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
623
        return list != null && IteratorUtil.anyMatch(list.iterator(), predicate);
1✔
624
    }
625

626
    /**
627
     * Returns true if all elements of the iterable match the predicate. Return
628
     * true if the list is null or empty.
629
     */
630
    public static <N> boolean all(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
631
        return list == null || IteratorUtil.allMatch(list.iterator(), predicate);
×
632
    }
633

634
    /**
635
     * Returns true if no element of the iterable matches the predicate. Return
636
     * true if the list is null or empty.
637
     */
638
    public static <N> boolean none(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
639
        return list == null || IteratorUtil.noneMatch(list.iterator(), predicate);
×
640
    }
641

642
    /**
643
     * If the set has a single element, returns it, otherwise returns null.
644
     * Obviously the set should not contain null elements.
645
     */
646
    public static <@NonNull T> @Nullable T asSingle(Set<T> set) {
647
        if (set.size() == 1) {
×
648
            return set.iterator().next();
×
649
        } else {
650
            return null;
×
651
        }
652
    }
653

654
    /**
655
     * Returns an unmodifiable copy of the list. This is to be preferred
656
     * to {@link Collections#unmodifiableList(List)} if you don't trust
657
     * the source of the list, because no one holds a reference to the buffer
658
     * except the returned unmodifiable list.
659
     *
660
     * @param list A list
661
     * @param <T>  Type of items
662
     */
663
    public static <T> List<T> defensiveUnmodifiableCopy(List<? extends T> list) {
664
        if (list instanceof PSequence) {
1✔
665
            return (List<T>) list; // is already immutable
1✔
666
        }
667
        if (list.isEmpty()) {
×
668
            return ConsPStack.empty();
×
669
        } else if (list.size() == 1) {
×
670
            return ConsPStack.singleton(list.get(0));
×
671
        }
672
        return ConsPStack.from(list);
×
673
    }
674

675
    public static <T> Set<T> defensiveUnmodifiableCopyToSet(Collection<? extends T> list) {
676
        if (list.isEmpty()) {
×
677
            return emptySet();
×
678
        }
679
        return Collections.unmodifiableSet(new LinkedHashSet<>(list));
×
680
    }
681

682
    /**
683
     * Like {@link String#join(CharSequence, Iterable)}, except it appends
684
     * on a preexisting {@link StringBuilder}. The result value is that StringBuilder.
685
     */
686
    public static <T> StringBuilder joinOn(StringBuilder sb,
687
                                           Iterable<? extends T> iterable,
688
                                           BiConsumer<? super StringBuilder, ? super T> appendItem,
689
                                           String delimiter) {
690
        boolean first = true;
1✔
691
        for (T t : iterable) {
1✔
692
            if (first) {
1✔
693
                first = false;
1✔
694
            } else {
695
                sb.append(delimiter);
1✔
696
            }
697
            appendItem.accept(sb, t);
1✔
698
        }
1✔
699
        return sb;
1✔
700
    }
701

702
    public static @NonNull StringBuilder joinCharsIntoStringBuilder(List<Chars> lines, String delimiter) {
703
        return joinOn(
1✔
704
            new StringBuilder(),
705
            lines,
706
            (buf, line) -> line.appendChars(buf),
1✔
707
            delimiter
708
        );
709
    }
710

711

712
    /**
713
     * Merge the second map into the first. If some keys are in common,
714
     * merge them using the merge function, like {@link Map#merge(Object, Object, BiFunction)}.
715
     */
716
    public static <K, V> void mergeMaps(Map<K, V> result, Map<K, V> other, BinaryOperator<V> mergeFun) {
717
        for (K otherKey : other.keySet()) {
×
718
            V otherInfo = other.get(otherKey); // non-null
×
719
            result.merge(otherKey, otherInfo, mergeFun);
×
720
        }
×
721
    }
×
722

723
    public static @NonNull <T> List<T> makeUnmodifiableAndNonNull(@Nullable List<? extends T> list) {
724
        if (list instanceof PSequence) {
×
725
            return (List<T>) list;
×
726
        }
727
        return list == null || list.isEmpty() ? emptyList()
×
728
                                              : Collections.unmodifiableList(list);
×
729
    }
730
}
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