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