View Javadoc
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:olamy@codehaus.org">olamy</a>
31   *
32   */
33  public class CollectionUtils
34  {
35      // ----------------------------------------------------------------------
36      // Static methods that can probably be moved to a real util class.
37      // ----------------------------------------------------------------------
38  
39      /**
40       * Take a dominant and recessive Map and merge the key:value pairs where the recessive Map may add key:value pairs
41       * to the dominant Map but may not override any existing key:value pairs. If we have two Maps, a dominant and
42       * recessive, and their respective keys are as follows: dominantMapKeys = { a, b, c, d, e, f } recessiveMapKeys = {
43       * a, b, c, x, y, z } Then the result should be the following: resultantKeys = { a, b, c, d, e, f, x, y, z }
44       *
45       * @param dominantMap Dominant Map.
46       * @param recessiveMap Recessive Map.
47       * @param <K> type
48       * @param <V> type
49       * @return The result map with combined dominant and recessive values.
50       */
51      public static <K, V> Map<K, V> mergeMaps( Map<K, V> dominantMap, Map<K, V> recessiveMap )
52      {
53  
54          if ( dominantMap == null && recessiveMap == null )
55          {
56              return null;
57          }
58  
59          if ( dominantMap != null && recessiveMap == null )
60          {
61              return dominantMap;
62          }
63  
64          if ( dominantMap == null )
65          {
66              return recessiveMap;
67          }
68  
69          Map<K, V> result = new HashMap<>();
70  
71          // Grab the keys from the dominant and recessive maps.
72          Set<K> dominantMapKeys = dominantMap.keySet();
73          Set<K> recessiveMapKeys = recessiveMap.keySet();
74  
75          // Create the set of keys that will be contributed by the
76          // recessive Map by subtracting the intersection of keys
77          // from the recessive Map's keys.
78          Collection<K> contributingRecessiveKeys =
79              CollectionUtils.subtract( recessiveMapKeys,
80                                        CollectionUtils.intersection( dominantMapKeys, recessiveMapKeys ) );
81  
82          result.putAll( dominantMap );
83  
84          // Now take the keys we just found and extract the values from
85          // the recessiveMap and put the key:value pairs into the dominantMap.
86          for ( K key : contributingRecessiveKeys )
87          {
88              result.put( key, recessiveMap.get( key ) );
89          }
90  
91          return result;
92      }
93  
94      /**
95       * Take a series of <code>Map</code>s and merge them where the ordering of the array from 0..n is the dominant
96       * order.
97       *
98       * @param maps An array of Maps to merge.
99       * @param <K> type
100      * @param <V> type
101      * @return Map The result Map produced after the merging process.
102      */
103     public static <K, V> Map<K, V> mergeMaps( Map<K, V>[] maps )
104     {
105         Map<K, V> result;
106 
107         if ( maps.length == 0 )
108         {
109             result = null;
110         }
111         else if ( maps.length == 1 )
112         {
113             result = maps[0];
114         }
115         else
116         {
117             result = mergeMaps( maps[0], maps[1] );
118 
119             for ( int i = 2; i < maps.length; i++ )
120             {
121                 result = mergeMaps( result, maps[i] );
122             }
123         }
124 
125         return result;
126     }
127 
128     /**
129      * <p>
130      * Returns a {@link Collection} containing the intersection of the given {@link Collection}s.
131      * </p>
132      * The cardinality of each element in the returned {@link Collection} will be equal to the minimum of the
133      * cardinality of that element in the two given {@link Collection}s.
134      *
135      * @param a The first collection
136      * @param b The second collection
137      * @param <E> the type
138      * @see Collection#retainAll
139      * @return The intersection of a and b, never null
140      */
141     public static <E> Collection<E> intersection( final Collection<E> a, final Collection<E> b )
142     {
143         ArrayList<E> list = new ArrayList<>();
144         Map<E, Integer> mapa = getCardinalityMap( a );
145         Map<E, Integer> mapb = getCardinalityMap( b );
146         Set<E> elts = new HashSet<>( a );
147         elts.addAll( b );
148         for ( E obj : elts )
149         {
150             for ( int i = 0, m = Math.min( getFreq( obj, mapa ), getFreq( obj, mapb ) ); i < m; i++ )
151             {
152                 list.add( obj );
153             }
154         }
155         return list;
156     }
157 
158     /**
159      * Returns a {@link Collection} containing <code>a - b</code>. The cardinality of each element <i>e</i> in
160      * the returned {@link Collection} will be the cardinality of <i>e</i> in <i>a</i> minus the cardinality of <i>e</i>
161      * in <i>b</i>, or zero, whichever is greater.
162      *
163      * @param a The start collection
164      * @param b The collection that will be subtracted
165      * @param <T> the type
166      * @see Collection#removeAll
167      * @return The result of the subtraction
168      */
169     public static <T> Collection<T> subtract( final Collection<T> a, final Collection<T> b )
170     {
171         ArrayList<T> list = new ArrayList<>( a );
172         for ( T aB : b )
173         {
174             list.remove( aB );
175         }
176         return list;
177     }
178 
179     /**
180      * Returns a {@link Map} mapping each unique element in the given {@link Collection} to an {@link Integer}
181      * representing the number of occurrences of that element in the {@link Collection}. An entry that maps to
182      * <code>null</code> indicates that the element does not appear in the given {@link Collection}.
183      * 
184      * @param col The collection to count cardinalities for
185      * @param <E> the type
186      * @return A map of counts, indexed on each element in the collection
187      */
188     public static <E> Map<E, Integer> getCardinalityMap( final Collection<E> col )
189     {
190         HashMap<E, Integer> count = new HashMap<>();
191         for ( E obj : col )
192         {
193             Integer c = count.get( obj );
194             if ( null == c )
195             {
196                 count.put( obj, 1 );
197             }
198             else
199             {
200                 count.put( obj, c + 1 );
201             }
202         }
203         return count;
204     }
205 
206     public static <E> List<E> iteratorToList( Iterator<E> it )
207     {
208         if ( it == null )
209         {
210             throw new NullPointerException( "it cannot be null." );
211         }
212 
213         List<E> list = new ArrayList<E>();
214 
215         while ( it.hasNext() )
216         {
217             list.add( it.next() );
218         }
219 
220         return list;
221     }
222 
223     // ----------------------------------------------------------------------
224     //
225     // ----------------------------------------------------------------------
226 
227     private static <E> int getFreq( final E obj, final Map<E, Integer> freqMap )
228     {
229         try
230         {
231             Integer o = freqMap.get( obj );
232             if ( o != null ) // minimize NullPointerExceptions
233             {
234                 return o;
235             }
236         }
237         catch ( NullPointerException | NoSuchElementException ignore )
238         {
239         }
240         return 0;
241     }
242 }