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