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