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