001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
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    
029    package edu.wisc.ssec.mcidasv.util;
030    
031    import java.lang.reflect.Array;
032    import java.util.ArrayList;
033    import java.util.Collection;
034    import java.util.Collections;
035    import java.util.HashMap;
036    import java.util.HashSet;
037    import java.util.Iterator;
038    import java.util.LinkedHashMap;
039    import java.util.LinkedHashSet;
040    import java.util.LinkedList;
041    import java.util.List;
042    import java.util.Map;
043    import java.util.Set;
044    import java.util.concurrent.ConcurrentHashMap;
045    import java.util.concurrent.CopyOnWriteArrayList;
046    import java.util.concurrent.CopyOnWriteArraySet;
047    
048    import 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"})
058    public 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<E>(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<E>();
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<E>(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<E>(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<E>();
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<E>(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<E>(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<K, V>();
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<K, V>(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<K, V>(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<K, V>();
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<K, V>(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<K, V>(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<K,V>();
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<E>();
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<E>(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<E>(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<E>();
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<E>(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<E>();
531            Collections.addAll(set, elems);
532            return set;
533        }
534    
535        public static <E> List<E> linkedList() {
536            return new LinkedList<E>();
537        }
538    
539        public static <E> List<E> linkedList(final Collection<? extends E> c) {
540            return new LinkedList<E>(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<E>();
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<E>(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<E>(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<K, V>(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<K, V>(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    }