1 package org.codehaus.plexus.util; 2 3 /* 4 * Copyright The Codehaus Foundation. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.HashMap; 22 import java.util.HashSet; 23 import java.util.Iterator; 24 import java.util.List; 25 import java.util.Map; 26 import java.util.NoSuchElementException; 27 import java.util.Set; 28 29 /** 30 * @author <a href="mailto:[email protected]">olamy</a> 31 * 32 */ 33 public class CollectionUtils { 34 // ---------------------------------------------------------------------- 35 // Static methods that can probably be moved to a real util class. 36 // ---------------------------------------------------------------------- 37 38 /** 39 * Take a dominant and recessive Map and merge the key:value pairs where the recessive Map may add key:value pairs 40 * to the dominant Map but may not override any existing key:value pairs. If we have two Maps, a dominant and 41 * recessive, and their respective keys are as follows: dominantMapKeys = { a, b, c, d, e, f } recessiveMapKeys = { 42 * a, b, c, x, y, z } Then the result should be the following: resultantKeys = { a, b, c, d, e, f, x, y, z } 43 * 44 * @param dominantMap Dominant Map. 45 * @param recessiveMap Recessive Map. 46 * @param <K> type 47 * @param <V> type 48 * @return The result map with combined dominant and recessive values. 49 */ 50 public static <K, V> Map<K, V> mergeMaps(Map<K, V> dominantMap, Map<K, V> recessiveMap) { 51 52 if (dominantMap == null && recessiveMap == null) { 53 return null; 54 } 55 56 if (dominantMap != null && recessiveMap == null) { 57 return dominantMap; 58 } 59 60 if (dominantMap == null) { 61 return recessiveMap; 62 } 63 64 Map<K, V> result = new HashMap<>(); 65 66 // Grab the keys from the dominant and recessive maps. 67 Set<K> dominantMapKeys = dominantMap.keySet(); 68 Set<K> recessiveMapKeys = recessiveMap.keySet(); 69 70 // Create the set of keys that will be contributed by the 71 // recessive Map by subtracting the intersection of keys 72 // from the recessive Map's keys. 73 Collection<K> contributingRecessiveKeys = CollectionUtils.subtract( 74 recessiveMapKeys, CollectionUtils.intersection(dominantMapKeys, recessiveMapKeys)); 75 76 result.putAll(dominantMap); 77 78 // Now take the keys we just found and extract the values from 79 // the recessiveMap and put the key:value pairs into the dominantMap. 80 for (K key : contributingRecessiveKeys) { 81 result.put(key, recessiveMap.get(key)); 82 } 83 84 return result; 85 } 86 87 /** 88 * Take a series of <code>Map</code>s and merge them where the ordering of the array from 0..n is the dominant 89 * order. 90 * 91 * @param maps An array of Maps to merge. 92 * @param <K> type 93 * @param <V> type 94 * @return Map The result Map produced after the merging process. 95 */ 96 public static <K, V> Map<K, V> mergeMaps(Map<K, V>[] maps) { 97 Map<K, V> result; 98 99 if (maps.length == 0) { 100 result = null; 101 } else if (maps.length == 1) { 102 result = maps[0]; 103 } else { 104 result = mergeMaps(maps[0], maps[1]); 105 106 for (int i = 2; i < maps.length; i++) { 107 result = mergeMaps(result, maps[i]); 108 } 109 } 110 111 return result; 112 } 113 114 /** 115 * <p> 116 * Returns a {@link Collection} containing the intersection of the given {@link Collection}s. 117 * </p> 118 * The cardinality of each element in the returned {@link Collection} will be equal to the minimum of the 119 * cardinality of that element in the two given {@link Collection}s. 120 * 121 * @param a The first collection 122 * @param b The second collection 123 * @param <E> the type 124 * @see Collection#retainAll 125 * @return The intersection of a and b, never null 126 */ 127 public static <E> Collection<E> intersection(final Collection<E> a, final Collection<E> b) { 128 ArrayList<E> list = new ArrayList<>(); 129 Map<E, Integer> mapa = getCardinalityMap(a); 130 Map<E, Integer> mapb = getCardinalityMap(b); 131 Set<E> elts = new HashSet<>(a); 132 elts.addAll(b); 133 for (E obj : elts) { 134 for (int i = 0, m = Math.min(getFreq(obj, mapa), getFreq(obj, mapb)); i < m; i++) { 135 list.add(obj); 136 } 137 } 138 return list; 139 } 140 141 /** 142 * Returns a {@link Collection} containing <code>a - b</code>. The cardinality of each element <i>e</i> in 143 * the returned {@link Collection} will be the cardinality of <i>e</i> in <i>a</i> minus the cardinality of <i>e</i> 144 * in <i>b</i>, or zero, whichever is greater. 145 * 146 * @param a The start collection 147 * @param b The collection that will be subtracted 148 * @param <T> the type 149 * @see Collection#removeAll 150 * @return The result of the subtraction 151 */ 152 public static <T> Collection<T> subtract(final Collection<T> a, final Collection<T> b) { 153 ArrayList<T> list = new ArrayList<>(a); 154 for (T aB : b) { 155 list.remove(aB); 156 } 157 return list; 158 } 159 160 /** 161 * Returns a {@link Map} mapping each unique element in the given {@link Collection} to an {@link Integer} 162 * representing the number of occurrences of that element in the {@link Collection}. An entry that maps to 163 * <code>null</code> indicates that the element does not appear in the given {@link Collection}. 164 * 165 * @param col The collection to count cardinalities for 166 * @param <E> the type 167 * @return A map of counts, indexed on each element in the collection 168 */ 169 public static <E> Map<E, Integer> getCardinalityMap(final Collection<E> col) { 170 HashMap<E, Integer> count = new HashMap<>(); 171 for (E obj : col) { 172 Integer c = count.get(obj); 173 if (null == c) { 174 count.put(obj, 1); 175 } else { 176 count.put(obj, c + 1); 177 } 178 } 179 return count; 180 } 181 182 public static <E> List<E> iteratorToList(Iterator<E> it) { 183 if (it == null) { 184 throw new NullPointerException("it cannot be null."); 185 } 186 187 List<E> list = new ArrayList<E>(); 188 189 while (it.hasNext()) { 190 list.add(it.next()); 191 } 192 193 return list; 194 } 195 196 // ---------------------------------------------------------------------- 197 // 198 // ---------------------------------------------------------------------- 199 200 private static <E> int getFreq(final E obj, final Map<E, Integer> freqMap) { 201 try { 202 Integer o = freqMap.get(obj); 203 if (o != null) // minimize NullPointerExceptions 204 { 205 return o; 206 } 207 } catch (NullPointerException | NoSuchElementException ignore) { 208 } 209 return 0; 210 } 211 }