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