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

pmd / pmd / 119

15 Aug 2025 10:06AM UTC coverage: 78.488% (+0.01%) from 78.476%
119

push

github

adangel
[java] ShortVariable - improve detection of unnamed variables

Refs #5914

Co-authored-by: Juan Martín Sotuyo Dodero <juan.sotuyo@pedidosya.com>

17911 of 23661 branches covered (75.7%)

Branch coverage included in aggregate %.

39213 of 49120 relevant lines covered (79.83%)

0.81 hits per line

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

35.65
/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
    public static <K, V> Map<K, V> buildMap(Consumer<Map<K, V>> effect) {
237
        Map<K, V> map = new LinkedHashMap<>();
1✔
238
        effect.accept(map);
1✔
239
        return Collections.unmodifiableMap(map);
1✔
240
    }
241

242
    public static <K, V> Map<K, V> buildMap(Map<K, V> initialMap, Consumer<Map<K, V>> effect) {
243
        Map<K, V> map = new LinkedHashMap<>(initialMap);
1✔
244
        effect.accept(map);
1✔
245
        return Collections.unmodifiableMap(map);
1✔
246
    }
247

248
    public static <T, R> List<@NonNull R> mapNotNull(Iterable<? extends T> from, Function<? super T, ? extends @Nullable R> f) {
249
        Iterator<? extends T> it = from.iterator();
×
250
        if (!it.hasNext()) {
×
251
            return Collections.emptyList();
×
252
        }
253
        List<R> res = new ArrayList<>();
×
254
        while (it.hasNext()) {
×
255
            R r = f.apply(it.next());
×
256
            if (r != null) {
×
257
                res.add(r);
×
258
            }
259
        }
×
260
        return res;
×
261
    }
262

263
    /**
264
     * Produce a new map with the mappings of the first, and one additional
265
     * mapping. The returned map may be unmodifiable.
266
     */
267
    public static <K, V> Map<K, V> plus(Map<K, V> m, K k, V v) {
268
        AssertionUtil.requireParamNotNull("map", m);
×
269
        if (m instanceof PMap) {
×
270
            return ((PMap<K, V>) m).plus(k, v);
×
271
        }
272
        if (m.isEmpty()) {
×
273
            return Collections.singletonMap(k, v);
×
274
        }
275
        Map<K, V> newM = new HashMap<>(m);
×
276
        newM.put(k, v);
×
277
        return newM;
×
278
    }
279

280
    /**
281
     * Produce a new list with the elements of the first, and one additional
282
     * item. The returned list is immutable.
283
     */
284
    public static <V> List<V> plus(List<V> list, V v) {
285
        if (list instanceof PSequence) {
×
286
            return ((PSequence<V>) list).plus(v);
×
287
        } else if (list.isEmpty()) {
×
288
            return ConsPStack.singleton(v);
×
289
        }
290
        return ConsPStack.from(list).plus(v);
×
291
    }
292

293
    /** Returns the empty list. */
294
    public static <V> List<V> emptyList() {
295
        // We use this implementation so that it plays well with other
296
        // operations that expect immutable data.
297
        return ConsPStack.empty();
1✔
298
    }
299

300
    /**
301
     * Returns an unmodifiable set containing the set union of the collection,
302
     * and the new elements.
303
     */
304
    @SafeVarargs
305
    @SuppressWarnings("unchecked")
306
    public static <V> Set<V> setUnion(Collection<? extends V> set, V first, V... newElements) {
307
        if (set instanceof PSet) {
1!
308
            return ((PSet<V>) set).plus(first).plusAll(asList(newElements));
×
309
        }
310
        Set<V> newSet = new LinkedHashSet<>(set.size() + 1 + newElements.length);
1✔
311
        newSet.addAll(set);
1✔
312
        newSet.add(first);
1✔
313
        Collections.addAll(newSet, newElements);
1✔
314
        return Collections.unmodifiableSet(newSet);
1✔
315
    }
316

317
    /**
318
     * Returns the key that corresponds to the given value in the map,
319
     * or null if it is not contained in the map.
320
     *
321
     * @param m   Map
322
     * @param v   Value
323
     * @param <K> Type of keys
324
     * @param <V> Type of values
325
     *
326
     * @throws NullPointerException If the entry is found, but the key
327
     *                              is null
328
     * @throws NullPointerException If the map is null
329
     */
330
    public static <@NonNull K, V> @Nullable K getKeyOfValue(Map<K, V> m, V v) {
331
        AssertionUtil.requireParamNotNull("map", m);
×
332
        for (Entry<K, V> it : m.entrySet()) {
×
333
            if (it.getValue().equals(v)) {
×
334
                return Objects.requireNonNull(it.getKey(), "This method uses null as a sentinel value");
×
335
            }
336
        }
×
337
        return null;
×
338
    }
339

340

341
    /**
342
     * Returns a map associating each key in the first list to its
343
     * corresponding value in the second.
344
     *
345
     * @throws IllegalArgumentException If the list size are mismatched
346
     * @throws NullPointerException     If either of the parameter is null,
347
     *                                  or any of the keys or values are null
348
     */
349
    public static <K, V> Map<K, V> zip(List<? extends @NonNull K> from, List<? extends @NonNull V> to) {
350
        AssertionUtil.requireParamNotNull("keys", from);
×
351
        AssertionUtil.requireParamNotNull("values", to);
×
352
        Validate.isTrue(from.size() == to.size(), "Mismatched list sizes %s to %s", from, to);
×
353

354
        if (from.isEmpty()) {
×
355
            return emptyMap();
×
356
        }
357

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

360
        for (int i = 0; i < from.size(); i++) {
×
361
            K key = from.get(i);
×
362
            V val = to.get(i);
×
363

364
            Validate.notNull(key);
×
365
            Validate.notNull(val);
×
366

367
            map.put(key, val);
×
368
        }
369

370
        return map;
×
371
    }
372

373
    public static <K, V> Map<K, V> associateWith(Collection<? extends @NonNull K> keys, Function<? super K, ? extends V> mapper) {
374
        AssertionUtil.requireParamNotNull("keys", keys);
×
375
        if (keys.isEmpty()) {
×
376
            return emptyMap();
×
377
        }
378

379
        return associateWithTo(new HashMap<>(keys.size()), keys, mapper);
×
380
    }
381

382
    public static <K, V> Map<K, V> associateWithTo(Map<K, V> collector, Collection<? extends @NonNull K> keys, Function<? super K, ? extends V> mapper) {
383
        AssertionUtil.requireParamNotNull("collector", collector);
×
384
        AssertionUtil.requireParamNotNull("keys", keys);
×
385
        AssertionUtil.requireParamNotNull("mapper", mapper);
×
386
        for (K key : keys) {
×
387
            collector.put(key, mapper.apply(key));
×
388
        }
×
389
        return collector;
×
390
    }
391

392

393
    public static <K, V> Map<K, V> associateBy(Collection<? extends @NonNull V> values, Function<? super V, ? extends K> keyMapper) {
394
        AssertionUtil.requireParamNotNull("values", values);
1✔
395
        if (values.isEmpty()) {
1✔
396
            return emptyMap();
1✔
397
        }
398

399
        return associateByTo(new HashMap<>(values.size()), values, keyMapper);
1✔
400
    }
401

402

403
    public static <K, V> Map<K, V> associateByTo(Map<K, V> collector, Collection<? extends @NonNull V> values, Function<? super V, ? extends K> keyMapper) {
404
        AssertionUtil.requireParamNotNull("collector", collector);
1✔
405
        AssertionUtil.requireParamNotNull("values", values);
1✔
406
        AssertionUtil.requireParamNotNull("keyMapper", keyMapper);
1✔
407
        for (V v : values) {
1✔
408
            collector.put(keyMapper.apply(v), v);
1✔
409
        }
1✔
410
        return collector;
1✔
411
    }
412

413
    /**
414
     * Map each element of the given collection with the given function,
415
     * and accumulates it into an unmodifiable list.
416
     */
417
    public static <T, R> List<R> map(Collection<? extends T> from, Function<? super T, ? extends R> f) {
418
        if (from == null) {
1!
419
            return emptyList();
×
420
        }
421
        return map(from.iterator(), from.size(), f);
1✔
422
    }
423

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

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

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

457
    private static <T, R> List<R> map(Iterator<? extends T> from, int sizeHint, Function<? super T, ? extends R> f) {
458
        if (!from.hasNext()) {
1✔
459
            return emptyList();
1✔
460
        } else if (sizeHint == 1) {
1✔
461
            return ConsPStack.singleton(f.apply(from.next()));
1✔
462
        }
463
        List<R> res = sizeHint == UNKNOWN_SIZE ? new ArrayList<>() : new ArrayList<>(sizeHint);
1✔
464
        while (from.hasNext()) {
1✔
465
            res.add(f.apply(from.next()));
1✔
466
        }
467
        return Collections.unmodifiableList(res);
1✔
468
    }
469

470
    /**
471
     * Map each element of the given iterable with the given function,
472
     * and accumulates it into the collector.
473
     */
474
    public static <T, U, A, C> C map(Collector<? super U, A, ? extends C> collector,
475
                                     Iterable<? extends T> from,
476
                                     Function<? super T, ? extends U> f) {
477
        if (from == null) {
1!
478
            return map(collector, emptyIterator(), f);
×
479
        }
480
        return map(collector, from.iterator(), f);
1✔
481
    }
482

483
    /**
484
     * Map each element of the given iterator with the given function,
485
     * and accumulates it into the collector.
486
     */
487
    // one more type param and we can write tupac
488
    public static <T, U, A, C> C map(Collector<? super U, A, ? extends C> collector,
489
                                     Iterator<? extends T> from,
490
                                     Function<? super T, ? extends U> f) {
491
        A a = collector.supplier().get();
1✔
492
        BiConsumer<A, ? super U> accumulator = collector.accumulator();
1✔
493
        from.forEachRemaining(t -> accumulator.accept(a, f.apply(t)));
1✔
494
        return finish(collector, a);
1✔
495
    }
496

497
    /**
498
     * A collector that returns a mutable list. This contrasts with
499
     * {@link Collectors#toList()}, which makes no guarantee about the
500
     * mutability of the list.
501
     *
502
     * @param <T> Type of accumulated values
503
     */
504
    public static <T> Collector<T, ?, List<T>> toMutableList() {
505
        return Collectors.toCollection(ArrayList::new);
1✔
506
    }
507

508

509
    /**
510
     * A collector that returns a mutable set. This contrasts with
511
     * {@link Collectors#toSet()}, which makes no guarantee about the
512
     * mutability of the set. The set preserves insertion order.
513
     *
514
     * @param <T> Type of accumulated values
515
     */
516
    public static <T> Collector<T, ?, Set<T>> toMutableSet() {
517
        return Collectors.toCollection(LinkedHashSet::new);
1✔
518
    }
519

520
    /**
521
     * A collector that returns an unmodifiable list. This contrasts with
522
     * {@link Collectors#toList()}, which makes no guarantee about the
523
     * mutability of the list. {@code Collectors::toUnmodifiableList} was
524
     * only added in JDK 9.
525
     *
526
     * @param <T> Type of accumulated values
527
     */
528
    public static <T> Collector<T, ?, List<T>> toUnmodifiableList() {
529
        return Collectors.collectingAndThen(toMutableList(), Collections::unmodifiableList);
1✔
530
    }
531

532
    /**
533
     * A collector that returns an unmodifiable set. This contrasts with
534
     * {@link Collectors#toSet()}, which makes no guarantee about the
535
     * mutability of the set. {@code Collectors::toUnmodifiableSet} was
536
     * only added in JDK 9. The set preserves insertion order.
537
     *
538
     * @param <T> Type of accumulated values
539
     */
540
    public static <T> Collector<T, ?, Set<T>> toUnmodifiableSet() {
541
        return Collectors.collectingAndThen(toMutableSet(), Collections::unmodifiableSet);
1✔
542
    }
543

544
    /**
545
     * A collectors that accumulates into a persistent set.
546
     *
547
     * @param <T> Type of accumulated values
548
     */
549
    public static <T> Collector<T, ?, PSet<T>> toPersistentSet() {
550
        class Holder {
×
551

552
            PSet<T> set = HashTreePSet.empty();
×
553
        }
554

555
        return Collector.of(
×
556
            Holder::new,
×
557
            (h, t) -> {
558
                h.set = h.set.plus(t);
×
559
            },
×
560
            (left, right) -> {
561
                left.set = left.set.plusAll(right.set);
×
562
                return left;
×
563
            },
564
            a -> a.set
×
565
        );
566
    }
567

568
    /**
569
     * Finish the accumulated value of the collector.
570
     */
571
    public static <V, A, C> C finish(Collector<? super V, A, ? extends C> collector, A acc) {
572
        if (collector.characteristics().contains(Characteristics.IDENTITY_FINISH)) {
1!
573
            return (C) acc;
1✔
574
        } else {
575
            return collector.finisher().apply(acc);
×
576
        }
577
    }
578

579
    public static <T> List<T> drop(List<T> list, int n) {
580
        AssertionUtil.requireNonNegative("n", n);
1✔
581

582
        return list.size() <= n ? emptyList()
1✔
583
                                : list.subList(n, list.size());
1✔
584
    }
585

586
    public static <T> List<T> take(List<T> list, int n) {
587
        AssertionUtil.requireNonNegative("n", n);
1✔
588
        return list.size() <= n ? list
1!
589
                                : list.subList(0, n);
1✔
590
    }
591

592

593
    public static <T> List<T> listOfNotNull(T t) {
594
        return t == null ? emptyList() : ConsPStack.singleton(t);
×
595
    }
596

597
    /**
598
     * Returns true if any element of the iterable matches the predicate. Return
599
     * false if the list is null or empty.
600
     */
601
    public static <N> boolean any(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
602
        return list != null && IteratorUtil.anyMatch(list.iterator(), predicate);
1✔
603
    }
604

605
    /**
606
     * Returns true if all elements of the iterable match the predicate. Return
607
     * true if the list is null or empty.
608
     */
609
    public static <N> boolean all(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
610
        return list == null || IteratorUtil.allMatch(list.iterator(), predicate);
×
611
    }
612

613
    /**
614
     * Returns true if no element of the iterable matches the predicate. Return
615
     * true if the list is null or empty.
616
     */
617
    public static <N> boolean none(@Nullable Iterable<? extends N> list, Predicate<? super N> predicate) {
618
        return list == null || IteratorUtil.noneMatch(list.iterator(), predicate);
×
619
    }
620

621
    /**
622
     * If the set has a single element, returns it, otherwise returns null.
623
     * Obviously the set should not contain null elements.
624
     */
625
    public static <@NonNull T> @Nullable T asSingle(Set<T> set) {
626
        if (set.size() == 1) {
×
627
            return set.iterator().next();
×
628
        } else {
629
            return null;
×
630
        }
631
    }
632

633
    /**
634
     * Returns an unmodifiable copy of the list. This is to be preferred
635
     * to {@link Collections#unmodifiableList(List)} if you don't trust
636
     * the source of the list, because no one holds a reference to the buffer
637
     * except the returned unmodifiable list.
638
     *
639
     * @param list A list
640
     * @param <T>  Type of items
641
     */
642
    public static <T> List<T> defensiveUnmodifiableCopy(List<? extends T> list) {
643
        if (list instanceof PSequence) {
×
644
            return (List<T>) list; // is already immutable
×
645
        }
646
        if (list.isEmpty()) {
×
647
            return ConsPStack.empty();
×
648
        } else if (list.size() == 1) {
×
649
            return ConsPStack.singleton(list.get(0));
×
650
        }
651
        return ConsPStack.from(list);
×
652
    }
653

654
    public static <T> Set<T> defensiveUnmodifiableCopyToSet(Collection<? extends T> list) {
655
        if (list.isEmpty()) {
×
656
            return emptySet();
×
657
        }
658
        return Collections.unmodifiableSet(new LinkedHashSet<>(list));
×
659
    }
660

661
    /**
662
     * Like {@link String#join(CharSequence, Iterable)}, except it appends
663
     * on a preexisting {@link StringBuilder}. The result value is that StringBuilder.
664
     */
665
    public static <T> StringBuilder joinOn(StringBuilder sb,
666
                                           Iterable<? extends T> iterable,
667
                                           BiConsumer<? super StringBuilder, ? super T> appendItem,
668
                                           String delimiter) {
669
        boolean first = true;
1✔
670
        for (T t : iterable) {
1✔
671
            if (first) {
1✔
672
                first = false;
1✔
673
            } else {
674
                sb.append(delimiter);
1✔
675
            }
676
            appendItem.accept(sb, t);
1✔
677
        }
1✔
678
        return sb;
1✔
679
    }
680

681
    public static @NonNull StringBuilder joinCharsIntoStringBuilder(List<Chars> lines, String delimiter) {
682
        return joinOn(
1✔
683
            new StringBuilder(),
684
            lines,
685
            (buf, line) -> line.appendChars(buf),
1✔
686
            delimiter
687
        );
688
    }
689

690

691
    /**
692
     * Merge the second map into the first. If some keys are in common,
693
     * merge them using the merge function, like {@link Map#merge(Object, Object, BiFunction)}.
694
     */
695
    public static <K, V> void mergeMaps(Map<K, V> result, Map<K, V> other, BinaryOperator<V> mergeFun) {
696
        for (K otherKey : other.keySet()) {
×
697
            V otherInfo = other.get(otherKey); // non-null
×
698
            result.merge(otherKey, otherInfo, mergeFun);
×
699
        }
×
700
    }
×
701

702
    /**
703
     * Union of two PSets, which avoids creating a new pset if possible.
704
     */
705
    public static <V> PSet<V> union(PSet<V> as, PSet<V> bs) {
706
        if (as.isEmpty()) {
×
707
            return bs;
×
708
        } else if (bs.isEmpty()) {
×
709
            return as;
×
710
        }
711
        return as.plusAll(bs);
×
712
    }
713

714
    public static @NonNull <T> List<T> makeUnmodifiableAndNonNull(@Nullable List<? extends T> list) {
715
        if (list instanceof PSequence) {
×
716
            return (List<T>) list;
×
717
        }
718
        return list == null || list.isEmpty() ? emptyList()
×
719
                                              : Collections.unmodifiableList(list);
×
720
    }
721
}
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