001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.util;
030
031import java.lang.reflect.Array;
032import java.util.ArrayList;
033import java.util.Collection;
034import java.util.Collections;
035import java.util.HashMap;
036import java.util.HashSet;
037import java.util.Iterator;
038import java.util.LinkedHashMap;
039import java.util.LinkedHashSet;
040import java.util.LinkedList;
041import java.util.List;
042import java.util.Map;
043import java.util.Objects;
044import java.util.Set;
045import java.util.concurrent.ConcurrentHashMap;
046import java.util.concurrent.CopyOnWriteArrayList;
047import java.util.concurrent.CopyOnWriteArraySet;
048import java.util.stream.Collectors;
049
050import edu.wisc.ssec.mcidasv.util.functional.Function;
051
052/**
053 * A <i>collection</i> (ugh) of static methods that make working with Java's 
054 * default collections a bit easier, or at least allows you to elide some of
055 * the redundancy of idiomatic Java.
056 * 
057 * <p>Make use of {@literal "static imports"} to omit even more needless code.
058 */
059@SuppressWarnings({"ClassWithoutLogger", "UnusedDeclaration"})
060public final class CollectionHelpers {
061
062    /** Never! */
063    private CollectionHelpers() {}
064
065    /**
066     * {@literal "Converts"} the incoming {@literal "varargs"} into an array.
067     * 
068     * <p>Useful for doing things like:
069     * {@code String[] strs = arr("hello", "how", "are", "you?");}
070     *
071     * @param <T> Type of items to be converted into an array.
072     * @param ts Items that will make up the elements of the returned array.
073     * Cannot be {@code null}, and (for now) the items should be of the 
074     * <i>same</i> type.
075     * 
076     * @return Array populated with each item from {@code ts}.
077     */
078    @SafeVarargs
079    public static <T> T[] arr(T... ts) { 
080        return ts;
081    }
082
083    /**
084     * Creates a {@link List} from incoming {@literal "varargs"}. Currently 
085     * uses {@link ArrayList} as the {@code List} implementation.
086     * 
087     * <p>Used like so:
088     * {@code List<String> listy = list("y", "helo", "thar");}
089     *
090     * @param <E> Type of items that will be added to the resulting collection.
091     * @param elements Items that will make up the elements of the returned
092     * {@code List}.
093     * 
094     * @return {@code List} whose elements are each item within 
095     * {@code elements}.
096     */
097    @SafeVarargs
098    public static <E> List<E> list(E... elements) {
099        List<E> newList = new ArrayList<>(elements.length);
100        Collections.addAll(newList, elements);
101        return newList;
102    }
103
104    /**
105     * Creates a {@link Set} from incoming {@literal "varargs"}. Currently uses
106     * {@link LinkedHashSet} as the {@code Set} implementation (to preserve 
107     * ordering).
108     * 
109     * <p>Used like so:<pre>
110     * for (String s : set("beep", "boop", "blorp")) { ... }</pre>
111     *
112     * @param <E> Type of items that will be added to the resulting collection.
113     * @param elements Items that will appear within the returned {@code Set}.
114     * Cannot be {@code null}, and (for now) the items should be of the 
115     * <i>same</i> type.
116     * 
117     * @return A {@code Set} containing the items in {@code elements}. Remember
118     * that {@code Set}s only contain <i>unique</i> elements!
119     */
120    @SafeVarargs
121    public static <E> Set<E> set(E... elements) {
122        Set<E> newSet = new LinkedHashSet<>(elements.length);
123        Collections.addAll(newSet, elements);
124        return newSet;
125    }
126
127    /**
128     * Creates a new {@code cl} instance (limited to things implementing 
129     * {@link Collection}) populated with the {@literal "varargs"}. Useful if
130     * you truly despise {@link #arr(Object...)}, {@link #list(Object...)}, 
131     * or {@link #set(Object...)}.
132     * 
133     * <p>Example: {@code Collection<Integer> ints = collect(PriorityBlockingQueue.class, 1, 2, 3);}
134     *
135     * @param <E> Type of items that will be added to the resulting collection.
136     * @param cl A (non-abstract!) class that implements {@code Collection}. Cannot be {@code null}.
137     * @param elements Objects that will be added to the collection.
138     * 
139     * @return An instance of {@code cl} containing the given objects.
140     * 
141     * @throws RuntimeException
142     * if {@link java.lang.reflect.Constructor#newInstance(Object...) Constructor#newInstance(Object...)}
143     * had problems.
144     * 
145     * @see #arr(Object...)
146     * @see #list(Object...)
147     * @see #set(Object...)
148     */
149    @SuppressWarnings({ "unchecked", "rawtypes" }) // the only things being added to the collection are objects of type "E"
150    public static <E> Collection<E> collect(Class<? extends Collection> cl,
151                                            E... elements)
152    {
153        try {
154            Collection<E> c = cl.getConstructor().newInstance();
155            Collections.addAll(c, elements);
156            return c;
157        } catch (Exception e) {
158            throw new RuntimeException("Problem creating a new "+cl, e);
159        }
160    }
161
162    /**
163     * Determines the {@literal "length"} of a given object. This method 
164     * currently understands:<ul>
165     * <li>{@link Collection}</li>
166     * <li>{@link Map}</li> 
167     * <li>{@link CharSequence}</li>
168     * <li>{@link Array}</li>
169     * <li>{@link Iterable}</li>
170     * <li>{@link Iterator}</li>
171     * </ul>
172     * 
173     * <p>More coming!
174     * 
175     * @param o {@code Object} whose length we want. Cannot be {@code null}.
176     * 
177     * @return {@literal "Length"} of {@code o}.
178     * 
179     * @throws NullPointerException if {@code o} is {@code null}.
180     * @throws IllegalArgumentException if the method doesn't know how to test
181     * whatever type of object {@code o} might be.
182     */
183    @SuppressWarnings({"WeakerAccess"})
184    public static int len(final Object o) {
185        Objects.requireNonNull(o, "Null arguments do not have a length");
186        int len;
187        if (o instanceof Collection<?>) {
188            len = ((Collection<?>)o).size();
189        } else if (o instanceof Map<?, ?>) {
190            len = ((Map<?, ?>)o).size();
191        } else if (o instanceof CharSequence) {
192            len = ((CharSequence)o).length();
193        } else if (o instanceof Iterator<?>) {
194            int count = 0;
195            Iterator<?> it = (Iterator<?>)o;
196            while (it.hasNext()) {
197                it.next();
198                count++;
199            }
200            len = count;
201        } else if (o instanceof Iterable<?>) {
202            len = CollectionHelpers.len(((Iterable<?>)o).iterator());
203        } else {
204            throw new IllegalArgumentException("Don't know how to find the length of a " + o.getClass().getName());
205        }
206        return len;
207    }
208
209    /**
210     * Searches an object to see if it {@literal "contains"} another object.
211     * This method currently knows how to search:<ul>
212     * <li>{@link Collection}</li>
213     * <li>{@link Map}</li> 
214     * <li>{@link CharSequence}</li>
215     * <li>{@link Array}</li>
216     * <li>{@link Iterable}</li>
217     * <li>{@link Iterator}</li>
218     * </ul>
219     * 
220     * <p>More coming!
221     * 
222     * @param collection {@code Object} that will be searched for
223     * {@code item}. Cannot be {@code null}.
224     * @param item {@code Object} to search for within {@code o}.
225     * {@code null} values are allowed.
226     * 
227     * @return {@code true} if {@code o} contains {@code item}, {@code false}
228     * otherwise.
229     * 
230     * @throws NullPointerException if {@code o} is {@code null}.
231     * @throws IllegalArgumentException if the method doesn't know how to 
232     * search whatever type of object {@code o} might be.
233     */
234    // TODO(jon:89): item should probably become an array/collection too...
235    @SuppressWarnings({"WeakerAccess"})
236    public static boolean contains(final Object collection, final Object item) {
237        Objects.requireNonNull(collection, "Cannot search a null object.");
238        if (collection instanceof Collection<?>) {
239            return ((Collection<?>)collection).contains(item);
240        } else if ((collection instanceof String) && (item instanceof CharSequence)) {
241            return ((String)collection).contains((CharSequence) item);
242        } else if (collection instanceof Map<?, ?>) {
243            return ((Map<?, ?>)collection).containsKey(item);
244        } else if (collection instanceof Iterator<?>) {
245            Iterator<?> it = (Iterator<?>)collection;
246            if (item == null) {
247                while (it.hasNext()) {
248                    if (it.next() == null) {
249                        return true;
250                    }
251                }
252            } else {
253                while (it.hasNext()) {
254                    if (item.equals(it.next())) {
255                        return true;
256                    }
257                }
258            }
259            return false;
260        } else if (collection instanceof Iterable<?>) {
261            Iterator<?> it = ((Iterable<?>)collection).iterator();
262            return CollectionHelpers.contains(it, item);
263        } else if (collection.getClass().isArray()) {
264            for (int i = 0; i < Array.getLength(collection); i++) {
265                if (Array.get(collection, i).equals(item)) {
266                    return true;
267                }
268            }
269        }
270        throw new IllegalArgumentException("Don't know how to search a "+collection.getClass().getName());
271    }
272
273    /**
274     * Creates an empty {@link HashSet} that uses a little cleverness with 
275     * Java's generics. Useful for eliminating redundant type information and
276     * declaring fields as {@code final}.
277     *
278     * <p>Please consider using {@link #newHashSet(int)} or
279     * {@link #newHashSet(Collection)} instead of this method.
280     *
281     * @param <E> Type of items that will be added to the resulting collection.
282     *
283     * @return A new, empty {@code HashSet}.
284     *
285     * @see #newHashSet(int)
286     * @see #newHashSet(Collection)
287     */
288    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
289    public static <E> Set<E> newHashSet() {
290        return new HashSet<>();
291    }
292
293    /**
294     * Creates an empty {@link HashSet} with a given initial capacity.
295     *
296     * @param <E> Type of items that will be added to the resulting collection.
297     * @param initialCapacity Initial capacity of the {@code HashSet}. Cannot
298     * be negative.
299     *
300     * @return A new, empty {@code HashSet} with the given initial capacity.
301     */
302    public static <E> Set<E> newHashSet(int initialCapacity) {
303        return new HashSet<>(initialCapacity);
304    }
305
306    /**
307     * Copies an existing {@link Collection} into a new {@link HashSet}.
308     *
309     * @param <E> Type of items that will be added to the resulting collection.
310     * @param original {@code Collection} to be copied. Cannot be {@code null}.
311     *
312     * @return A new {@code HashSet} whose contents are the same as
313     * {@code original}.
314     */
315    public static <E> Set<E> newHashSet(Collection<E> original) {
316        return new HashSet<>(original);
317    }
318
319    /**
320     * Creates an empty {@link LinkedHashSet} that uses a little cleverness 
321     * with Java's generics. Useful for eliminating redundant type 
322     * information and declaring fields as {@code final}.
323     *
324     * <p>Please consider using {@link #newLinkedHashSet(int)} or
325     * {@link #newLinkedHashSet(Collection)} instead of this method.
326     *
327     * @param <E> Type of items that will be added to the resulting collection.
328     *
329     * @return A new, empty {@code LinkedHashSet}.
330     *
331     * @see #newLinkedHashSet(int)
332     * @see #newLinkedHashSet(Collection)
333     */
334    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
335    public static <E> Set<E> newLinkedHashSet() {
336        return new LinkedHashSet<>();
337    }
338
339    /**
340     * Creates an empty {@link LinkedHashSet} with a given initial capacity.
341     *
342     * @param <E> Type of items that will be added to the resulting collection.
343     * @param initialCapacity Initial capacity of the {@code LinkedHashSet}.
344     * Cannot be negative.
345     *
346     * @return A new, empty {@code LinkedHashSet} with the given initial
347     * capacity.
348     */
349    public static <E> Set<E> newLinkedHashSet(int initialCapacity) {
350        return new LinkedHashSet<>(initialCapacity);
351    }
352
353    /**
354     * Copies a {@link Collection} into a new {@link LinkedHashSet}.
355     *
356     * @param <E> Type of items that will be added to the resulting collection.
357     * @param original Collection to be copied. Cannot be {@code null}.
358     * 
359     * @return A new {@code LinkedHashSet} whose contents are the same as 
360     * {@code original}.
361     */
362    public static <E> Set<E> newLinkedHashSet(Collection<E> original) {
363        return new LinkedHashSet<>(original);
364    }
365
366    /**
367     * Creates an empty {@link HashSet} that uses a little cleverness with 
368     * Java's generics. Useful for eliminating redundant type information and
369     * declaring fields as {@code final}, while also reducing compiler warnings.
370     *
371     * <p>Please consider using {@link #newMap(int)} or
372     * {@link #newMap(Map)} instead of this method.
373     *
374     * @param <K> Type of keys that will be added to the {@code Map}.
375     * @param <V> Type of values that will be added to the {@code Map}.
376     *
377     * @return A new, empty {@code HashMap}.
378     *
379     * @see #newMap(int)
380     * @see #newMap(Map)
381     */
382    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
383    public static <K, V> Map<K, V> newMap() {
384        return new HashMap<>();
385    }
386
387    /**
388     * Creates an empty {@link HashSet} with a given initial capacity.
389     *
390     * @param <K> Type of keys that will be added to the {@code Map}.
391     * @param <V> Type of values that will be added to the {@code Map}.
392     * @param initialCapacity Initial capacity of the {@code HashMap}.
393     * Cannot be negative.
394     *
395     * @return A new, empty {@code HashMap} with the given initial capacity.
396     */
397    public static <K, V> Map<K, V> newMap(int initialCapacity) {
398        return new HashMap<>(initialCapacity);
399    }
400
401    /**
402     * Copies an existing {@link Map} into a new {@link HashMap}.
403     *
404     * @param <K> Type of keys that will be added to the {@code Map}.
405     * @param <V> Type of values that will be added to the {@code Map}.
406     * @param original Map to be copied. Cannot be {@code null}.
407     * 
408     * @return A new {@code HashMap} whose contents are the same as 
409     * {@code original}.
410     */
411    public static <K, V> Map<K, V> newMap(Map<K, V> original) {
412        return new HashMap<>(original);
413    }
414
415    /**
416     * Creates an empty {@link LinkedHashMap} that uses a little cleverness with
417     * Java's generics. Useful for eliminating redundant type information and 
418     * declaring fields as {@code final}, while also reducing compiler warnings.
419     *
420     * <p>Please consider using {@link #newLinkedHashMap(int)} or
421     * {@link #newLinkedHashSet(Collection)} instead of this method.
422     *
423     * @param <K> Type of keys that will be added to the {@code Map}.
424     * @param <V> Type of values that will be added to the {@code Map}.
425     *
426     * @return A new, empty {@code LinkedHashMap}.
427     *
428     * @see #newLinkedHashMap(int)
429     * @see #newLinkedHashMap(Map)
430     */
431    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
432    public static <K, V> Map<K, V> newLinkedHashMap() {
433        return new LinkedHashMap<>();
434    }
435
436    /**
437     * Creates an empty {@link LinkedHashMap} with a given initial capacity.
438     *
439     * @param <K> Type of keys that will be added to the {@code Map}.
440     * @param <V> Type of values that will be added to the {@code Map}.
441     * @param initialCapacity Initial capacity of the {@code LinkedHashMap}.
442     * Cannot be negative.
443     *
444     * @return A new, empty {@code LinkedHashMap} with the given initial
445     * capacity.
446     */
447    public static <K, V> Map<K, V> newLinkedHashMap(int initialCapacity) {
448        return new LinkedHashMap<>(initialCapacity);
449    }
450
451    /**
452     * Copies an existing {@link Map} into a new {@link LinkedHashMap}.
453     *
454     * @param <K> Type of keys that will be added to the {@code Map}.
455     * @param <V> Type of values that will be added to the {@code Map}.
456     * @param original Map to be copied. Cannot be {@code null}.
457     * 
458     * @return A new {@code LinkedHashMap} whose contents are the same as 
459     * {@code original}.
460     */
461    public static <K, V> Map<K, V> newLinkedHashMap(Map<K, V> original) {
462        return new LinkedHashMap<>(original);
463    }
464
465    /**
466     * Abuses Java's sad {@literal "type"} implementation to create a new
467     * {@link ConcurrentHashMap}.
468     *
469     * @param <K> Type of keys that will be added to the {@code Map}.
470     * @param <V> Type of values that will be added to the {@code Map}.
471     *
472     * @return Shiny and new {@code ConcurrentHashMap}
473     */
474    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
475    public static <K, V> Map<K, V> concurrentMap() {
476        return new ConcurrentHashMap<>();
477    }
478
479    /**
480     * Creates an empty {@link CopyOnWriteArrayList}. Keep in mind that you 
481     * only want to use {@code CopyOnWriteArrayList} for lists that are not 
482     * going to be modified very often!
483     *
484     * @param <E> Type of items that will be added to the resulting collection.
485     *
486     * @return A new, empty {@code CopyOnWriteArrayList}.
487     */
488    public static <E> List<E> concurrentList() {
489        return new CopyOnWriteArrayList<>();
490    }
491
492    /**
493     * Creates a new {@link CopyOnWriteArrayList} that contains all of the 
494     * elements in {@code original}. Keep in mind that you only want to use 
495     * {@code CopyOnWriteArrayList} for lists that are not going to be 
496     * modified very often!
497     *
498     * @param <E> Type of items that will be added to the resulting collection.
499     * @param original Collection to be copied into the new list.
500     * 
501     * @return A new {@code CopyOnWriteArrayList} whose contents are the same 
502     * as {@code original}.
503     */
504    public static <E> List<E> concurrentList(Collection<E> original) {
505        return new CopyOnWriteArrayList<>(original);
506    }
507
508    /**
509     * Creates a new {@link CopyOnWriteArrayList} from the incoming 
510     * {@literal "varargs"}. Keep in mind that you only want to use 
511     * {@code CopyOnWriteArrayList} for lists that are not going to be modified 
512     * very often!
513     *
514     * @param <E> Type of items that will be added to the resulting collection.
515     * @param elems Elements that will be contained in the resulting list.
516     * 
517     * @return A new {@code CopyOnWriteArrayList} that contains the incoming 
518     * objects.
519     */
520    @SafeVarargs
521    public static <E> List<E> concurrentList(E... elems) {
522        return new CopyOnWriteArrayList<>(elems);
523    }
524
525    /**
526     * Creates a new {@link CopyOnWriteArraySet}. Keep in mind that you only 
527     * want to use a {@code CopyOnWriteArraySet} for sets that are not going to
528     * be modified very often!
529     *
530     * @param <E> Type of items that will be added to the resulting collection.
531     *
532     * @return New, empty {@code CopyOnWriteArraySet}.
533     */
534    public static <E> Set<E> concurrentSet() {
535        return new CopyOnWriteArraySet<>();
536    }
537
538    /**
539     * Creates a new {@link CopyOnWriteArraySet} that contains all of the 
540     * elements in {@code original}. Keep in mind that you only want to use a 
541     * {@code CopyOnWriteArraySet} for sets that are not going to be modified 
542     * very often!
543     *
544     * @param <E> Type of items that will be added to the resulting collection.
545     * @param original Collection to be copied into the new set.
546     * 
547     * @return {@code CopyOnWriteArraySet} whose contents are the same as
548     * {@code original}.
549     */
550    public static <E> Set<E> concurrentSet(Collection<E> original) {
551        return new CopyOnWriteArraySet<>(original);
552    }
553
554    /**
555     * Creates a new {@link CopyOnWriteArraySet} from the incoming 
556     * {@literal "varargs"}. Keep in mind that you only want to use a 
557     * {@code CopyOnWriteArraySet} for sets that are not going to be modified 
558     * very often!
559     *
560     * @param <E> Type of items that will be added to the resulting collection.
561     * @param elems Elements that will be contained in the resulting set.
562     * 
563     * @return {@code CopyOnWriteArraySet} that contains the incoming objects.
564     */
565    @SafeVarargs
566    public static <E> Set<E> concurrentSet(E... elems) {
567        Set<E> set = new CopyOnWriteArraySet<>();
568        Collections.addAll(set, elems);
569        return set;
570    }
571
572    public static <E> List<E> linkedList() {
573        return new LinkedList<>();
574    }
575
576    public static <E> List<E> linkedList(final Collection<? extends E> c) {
577        return new LinkedList<>(c);
578    }
579
580    /**
581     * Creates an empty {@link ArrayList} that uses a little cleverness with
582     * Java's generics. Useful for eliminating redundant type information and
583     * declaring fields as {@code final}.
584     * 
585     * <p>Used like so:
586     * {@code List<String> listy = arrList();}
587     *
588     * <p>Please consider using {@link #arrList(int)} or
589     * {@link #arrList(Collection)} instead of this method.
590     *
591     * @param <E> Type of items that will be added to the resulting collection.
592     *
593     * @return A new, empty {@code ArrayList}.
594     *
595     * @see #arrList(int)
596     * @see #arrList(Collection)
597     */
598    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
599    public static <E> List<E> arrList() {
600        return new ArrayList<>();
601    }
602
603    /**
604     * Creates an empty {@link ArrayList} with a given capacity.
605     *
606     * @param <E> Type of items that will be added to the resulting collection.
607     * @param capacity The initial size of the returned {@code ArrayList}.
608     * 
609     * @return A new, empty {@code ArrayList} that has an initial capacity of
610     * {@code capacity} elements.
611     * 
612     * @see ArrayList#ArrayList(int)
613     */
614    public static <E> List<E> arrList(final int capacity) {
615        return new ArrayList<>(capacity);
616    }
617
618    /**
619     * Copies an existing {@link Collection} into a new {@link ArrayList}.
620     *
621     * @param <E> Type of items that will be added to the resulting collection.
622     * @param c {@code Collection} whose elements are to be placed into the 
623     * returned {@code ArrayList}.
624     * 
625     * @return An {@code ArrayList} containing the elements of {@code c}.
626     * 
627     * @see ArrayList#ArrayList(Collection)
628     */
629    public static <E> List<E> arrList(final Collection<? extends E> c) {
630        return new ArrayList<>(c);
631    }
632
633    /**
634     * Copies an existing {@link Collection} into a new (non-abstract!) 
635     * {@code Collection} class.
636     *
637     * @param <E> Type of items that will be added to the resulting collection.
638     * @param cl Non-abstract {@code Collection} class.
639     * @param old An existing {@code Collection}.
640     * 
641     * @return A new instance of {@code cl} that contains all of the elements
642     * from {@code old}.
643     * 
644     * @throws RuntimeException if there was trouble creating a new instance 
645     * of {@code cl}.
646     * 
647     * @see #collect(Class, Object...)
648     */
649    // not sure about the utility of this one...
650    @SuppressWarnings({ "unchecked", "rawtypes" }) // again, only adding items of type "E"
651    public static <E> Collection<E> recollect(Class<? extends Collection> cl,
652                                              Collection<E> old) 
653    {
654        try {
655            Collection<E> c = cl.getConstructor().newInstance();
656            c.addAll(old);
657            return c;
658        } catch (Exception e) {
659            throw new RuntimeException("", e);
660        }
661    }
662
663    /**
664     * Takes arrays of {@code keys} and {@code values} and merges them 
665     * together to form a {@link Map}. The returned {@code Map} is a 
666     * {@link LinkedHashMap} and is truncated in length to the length of the 
667     * shorter parameter.
668     * 
669     * <p>This is intended for use as {@literal "varargs"} supplied to 
670     * {@link #arr(Object...)}. Rather than doing something ugly like:
671     * <pre>
672     * Map&lt;String, String&gt; mappy = new LinkedHashMap&lt;String, String&gt;();
673     * mappy.put("key0", "val0");
674     * mappy.put("key1", "val1");
675     * ...
676     * mappy.put("keyN", "valN");
677     * </pre>
678     * 
679     * Simply do like so:
680     * <pre>
681     * mappy = zipMap(
682     *     arr("key0", "key1", ..., "keyN"),
683     *     arr("val0", "val1", ..., "valN"));
684     * </pre>
685     * 
686     * <p>The latter approach also allows you to make {@code static final} 
687     * {@link Map}s much more easily.
688     *
689     * @param <K> Type of keys that will be added to the {@code Map}.
690     * @param <V> Type of values that will be added to the {@code Map}.
691     * @param keys Array whose elements will be the keys in a {@code Map}.
692     * @param values Array whose elements will the values in a {@code Map}.
693     * 
694     * @return A {@code Map} whose entries are of the form 
695     * {@code keys[N], values[N]}.
696     * 
697     * @see #arr(Object...)
698     * @see #zipMap(Collection, Collection)
699     */
700    public static <K, V> Map<K, V> zipMap(K[] keys, V[] values) {
701        Map<K, V> zipped = new LinkedHashMap<>(keys.length);
702        for (int i = 0; (i < keys.length) && (i < values.length); i++) {
703            zipped.put(keys[i], values[i]);
704        }
705        return zipped;
706    }
707
708    /**
709     * A version of {@link #zipMap(Object[], Object[])} that works with 
710     * {@link Collection}s.
711     *
712     * @param <K> Type of keys that will be added to the {@code Map}.
713     * @param <V> Type of values that will be added to the {@code Map}.
714     * @param keys Items that will be the keys in the resulting {@code Map}.
715     * @param values Items that will be the values in the result {@code Map}.
716     * 
717     * @return A {@code Map} whose entries are of the form 
718     * {@code keys[N], values[N]}.
719     * 
720     * @see #zipMap(Object[], Object[])
721     */
722    public static <K, V> Map<K, V> zipMap(Collection<? extends K> keys,
723                                          Collection<? extends V> values)
724    {
725        Map<K, V> zipped = new LinkedHashMap<>(keys.size());
726        Iterator<? extends K> keyIterator = keys.iterator();
727        Iterator<? extends V> valueIterator = values.iterator();
728        while (keyIterator.hasNext() && valueIterator.hasNext()) {
729            zipped.put(keyIterator.next(), valueIterator.next());
730        }
731        return zipped;
732    }
733
734    /**
735     * Applies a given function to each item in a given list.
736     *
737     * @param <A> Type of items that will be passed into {@code f}.
738     * @param <B> Type of items that will be in the resulting {@code List}.
739     * @param f The {@link Function} to apply.
740     * @param as The list whose items are to be fed into {@code f}.
741     * 
742     * @return New list containing the results of each element of {@code as}
743     * being passed through {@code f}.
744     */
745    public static <A, B> List<B> map(final Function<A, B> f, List<A> as) {
746        List<B> bs = new ArrayList<>(as.size());
747        bs.addAll(as.stream().map(f::apply).collect(Collectors.toList()));
748        return bs;
749    }
750
751    /**
752     * Applies a given function to each item in a given {@link Set}.
753     *
754     * @param <A> Type of items that will be passed into {@code f}.
755     * @param <B> Type of items that will be in the resulting {@code Set}.
756     * @param f The {@link Function} to apply to {@code as}.
757     * @param as The {@code Set} whose items are to be fed into {@code f}.
758     * 
759     * @return New {@code Set} containing the results of passing each element
760     * in {@code as} through {@code f}.
761     */
762    public static <A, B> Set<B> map(final Function<A, B> f, Set<A> as) {
763        Set<B> bs = new LinkedHashSet<>(as.size());
764        bs.addAll(as.stream().map(f::apply).collect(Collectors.toList()));
765        return bs;
766    }
767
768    /**
769     * {@literal "Generics-friendly"} way to cast an object of some superclass
770     * ({@code A}) to a subclass or implementation ({@code B}). This method will
771     * fail if you attempt to cast to a type that is not a subclass of type
772     * {@code A}.
773     *
774     * <p>Example/Justification:<br>
775     * Consider a method like {@link ucar.unidata.xml.XmlUtil#findChildren(org.w3c.dom.Node, String) XmlUtil.findChildren(Node, String)}.<br/>
776     * Despite {@code findChildren} only returning lists containing {@code Node}
777     * objects, Java will generate a warning for the following code:
778     * <pre>
779     * import ucar.unidata.xml.XmlUtil;
780     * ....
781     * List&lt;Node&gt; nodes = XmlUtil.findChildren(panel, "blah");
782     * </pre>
783     * {@code cast} is a nice and terse way to avoid those warnings. Here's the
784     * previous example (with static imports of {@code cast} and {@code findChildren}):
785     * <pre>
786     * import static ucar.unidata.xml.XmlUtil.findChildren;
787     * import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.cast;
788     * ....
789     * List&lt;Node&gt; nodes = cast(findChildren(panel, "blah"));
790     * </pre>
791     *
792     * @param <A> Superclass of {@code B}. This is what you are
793     * {@literal "casting from"}...likely {@code Object} in most cases
794     * @param <B> Subclass of {@code A}. This is what you are
795     * {@literal "casting to"}.
796     *
797     * @param o The object whose type you are casting.
798     *
799     * @return {@code o}, casted from type {@code A} to {@code B}. Enjoy!
800     */
801    @SuppressWarnings("unchecked") public static <A, B extends A> B cast(A o) {
802        return (B)o;
803    }
804}