pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / java / org / apache / lucene / util / collections / IntToObjectMap.java
1 package org.apache.lucene.util.collections;
2
3 import java.util.Arrays;
4 import java.util.Iterator;
5
6 /**
7  * Licensed to the Apache Software Foundation (ASF) under one or more
8  * contributor license agreements.  See the NOTICE file distributed with
9  * this work for additional information regarding copyright ownership.
10  * The ASF licenses this file to You under the Apache License, Version 2.0
11  * (the "License"); you may not use this file except in compliance with
12  * the License.  You may obtain a copy of the License at
13  *
14  *     http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22
23 /**
24  * An Array-based hashtable which maps primitive int to Objects of generic type
25  * T.<br>
26  * The hashtable is constracted with a given capacity, or 16 as a default. In
27  * case there's not enough room for new pairs, the hashtable grows. <br>
28  * Capacity is adjusted to a power of 2, and there are 2 * capacity entries for
29  * the hash.
30  * 
31  * The pre allocated arrays (for keys, values) are at length of capacity + 1,
32  * when index 0 is used as 'Ground' or 'NULL'.<br>
33  * 
34  * The arrays are allocated ahead of hash operations, and form an 'empty space'
35  * list, to which the key,value pair is allocated.
36  * 
37  * @lucene.experimental
38  */
39 public class IntToObjectMap<T> implements Iterable<T> {
40
41   /**
42    * Implements an IntIterator which iterates over all the allocated indexes.
43    */
44   private final class IndexIterator implements IntIterator {
45     /**
46      * The last used baseHashIndex. Needed for "jumping" from one hash entry
47      * to another.
48      */
49     private int baseHashIndex = 0;
50
51     /**
52      * The next not-yet-visited index.
53      */
54     private int index = 0;
55
56     /**
57      * Index of the last visited pair. Used in {@link #remove()}.
58      */
59     private int lastIndex = 0;
60
61     /**
62      * Create the Iterator, make <code>index</code> point to the "first"
63      * index which is not empty. If such does not exist (eg. the map is
64      * empty) it would be zero.
65      */
66     public IndexIterator() {
67       for (baseHashIndex = 0; baseHashIndex < baseHash.length; ++baseHashIndex) {
68         index = baseHash[baseHashIndex];
69         if (index != 0) {
70           break;
71         }
72       }
73     }
74
75     public boolean hasNext() {
76       return (index != 0);
77     }
78
79     public int next() {
80       // Save the last index visited
81       lastIndex = index;
82
83       // next the index
84       index = next[index];
85
86       // if the next index points to the 'Ground' it means we're done with
87       // the current hash entry and we need to jump to the next one. This
88       // is done until all the hash entries had been visited.
89       while (index == 0 && ++baseHashIndex < baseHash.length) {
90         index = baseHash[baseHashIndex];
91       }
92
93       return lastIndex;
94     }
95
96     public void remove() {
97       IntToObjectMap.this.remove(keys[lastIndex]);
98     }
99
100   }
101
102   /**
103    * Implements an IntIterator, used for iteration over the map's keys.
104    */
105   private final class KeyIterator implements IntIterator {
106     private IntIterator iterator = new IndexIterator();
107
108     KeyIterator() { }
109     
110     public boolean hasNext() {
111       return iterator.hasNext();
112     }
113
114     public int next() {
115       return keys[iterator.next()];
116     }
117
118     public void remove() {
119       iterator.remove();
120     }
121   }
122
123   /**
124    * Implements an Iterator of a generic type T used for iteration over the
125    * map's values.
126    */
127   private final class ValueIterator implements Iterator<T> {
128     private IntIterator iterator = new IndexIterator();
129
130     ValueIterator() { }
131     
132     public boolean hasNext() {
133       return iterator.hasNext();
134     }
135
136     @SuppressWarnings("unchecked")
137     public T next() {
138       return (T) values[iterator.next()];
139     }
140
141     public void remove() {
142       iterator.remove();
143     }
144   }
145
146   /**
147    * Default capacity - in case no capacity was specified in the constructor
148    */
149   private static int defaultCapacity = 16;
150
151   /**
152    * Holds the base hash entries. if the capacity is 2^N, than the base hash
153    * holds 2^(N+1). It can hold
154    */
155   int[] baseHash;
156
157   /**
158    * The current capacity of the map. Always 2^N and never less than 16. We
159    * never use the zero index. It is needed to improve performance and is also
160    * used as "ground".
161    */
162   private int capacity;
163   /**
164    * All objects are being allocated at map creation. Those objects are "free"
165    * or empty. Whenever a new pair comes along, a pair is being "allocated" or
166    * taken from the free-linked list. as this is just a free list.
167    */
168   private int firstEmpty;
169
170   /**
171    * hashFactor is always (2^(N+1)) - 1. Used for faster hashing.
172    */
173   private int hashFactor;
174
175   /**
176    * This array holds the unique keys
177    */
178   int[] keys;
179
180   /**
181    * In case of collisions, we implement a double linked list of the colliding
182    * hash's with the following next[] and prev[]. Those are also used to store
183    * the "empty" list.
184    */
185   int[] next;
186
187   private int prev;
188
189   /**
190    * Number of currently objects in the map.
191    */
192   private int size;
193
194   /**
195    * This array holds the values
196    */
197   Object[] values;
198
199   /**
200    * Constructs a map with default capacity.
201    */
202   public IntToObjectMap() {
203     this(defaultCapacity);
204   }
205
206   /**
207    * Constructs a map with given capacity. Capacity is adjusted to a native
208    * power of 2, with minimum of 16.
209    * 
210    * @param capacity
211    *            minimum capacity for the map.
212    */
213   public IntToObjectMap(int capacity) {
214     this.capacity = 16;
215     // Minimum capacity is 16..
216     while (this.capacity < capacity) {
217       // Multiply by 2 as long as we're still under the requested capacity
218       this.capacity <<= 1;
219     }
220
221     // As mentioned, we use the first index (0) as 'Ground', so we need the
222     // length of the arrays to be one more than the capacity
223     int arrayLength = this.capacity + 1;
224
225     this.values = new Object[arrayLength];
226     this.keys = new int[arrayLength];
227     this.next = new int[arrayLength];
228
229     // Hash entries are twice as big as the capacity.
230     int baseHashSize = this.capacity << 1;
231
232     this.baseHash = new int[baseHashSize];
233
234     // The has factor is 2^M - 1 which is used as an "AND" hashing operator.
235     // {@link #calcBaseHash()}
236     this.hashFactor = baseHashSize - 1;
237
238     this.size = 0;
239
240     clear();
241   }
242
243   /**
244    * Adds a pair to the map. Takes the first empty position from the
245    * empty-linked-list's head - {@link firstEmpty}.
246    * 
247    * New pairs are always inserted to baseHash, and are followed by the old
248    * colliding pair.
249    * 
250    * @param key
251    *            integer which maps the given Object
252    * @param e
253    *            element which is being mapped using the given key
254    */
255   private void prvt_put(int key, T e) {
256     // Hash entry to which the new pair would be inserted
257     int hashIndex = calcBaseHashIndex(key);
258
259     // 'Allocating' a pair from the "Empty" list.
260     int objectIndex = firstEmpty;
261
262     // Setting data
263     firstEmpty = next[firstEmpty];
264     values[objectIndex] = e;
265     keys[objectIndex] = key;
266
267     // Inserting the new pair as the first node in the specific hash entry
268     next[objectIndex] = baseHash[hashIndex];
269     baseHash[hashIndex] = objectIndex;
270
271     // Announcing a new pair was added!
272     ++size;
273   }
274
275   /**
276    * Calculating the baseHash index using the internal <code>hashFactor</code>.
277    * 
278    * @param key
279    */
280   protected int calcBaseHashIndex(int key) {
281     return key & hashFactor;
282   }
283
284   /**
285    * Empties the map. Generates the "Empty" space list for later allocation.
286    */
287   public void clear() {
288     // Clears the hash entries
289     Arrays.fill(this.baseHash, 0);
290
291     // Set size to zero
292     size = 0;
293
294     // Mark all array entries as empty. This is done with
295     // <code>firstEmpty</code> pointing to the first valid index (1 as 0 is
296     // used as 'Ground').
297     firstEmpty = 1;
298
299     // And setting all the <code>next[i]</code> to point at
300     // <code>i+1</code>.
301     for (int i = 1; i < this.capacity;) {
302       next[i] = ++i;
303     }
304
305     // Surly, the last one should point to the 'Ground'.
306     next[this.capacity] = 0;
307   }
308
309   /**
310    * Checks if a given key exists in the map.
311    * 
312    * @param key
313    *            that is checked against the map data.
314    * @return true if the key exists in the map. false otherwise.
315    */
316   public boolean containsKey(int key) {
317     return find(key) != 0;
318   }
319
320   /**
321    * Checks if the given object exists in the map.<br>
322    * This method iterates over the collection, trying to find an equal object.
323    * 
324    * @param o
325    *            object that is checked against the map data.
326    * @return true if the object exists in the map (in .equals() meaning).
327    *         false otherwise.
328    */
329   public boolean containsValue(Object o) {
330     for (Iterator<T> iterator = iterator(); iterator.hasNext();) {
331       T object = iterator.next();
332       if (object.equals(o)) {
333         return true;
334       }
335     }
336     return false;
337   }
338
339   /**
340    * Find the actual index of a given key.
341    * 
342    * @param key
343    * @return index of the key. zero if the key wasn't found.
344    */
345   protected int find(int key) {
346     // Calculate the hash entry.
347     int baseHashIndex = calcBaseHashIndex(key);
348
349     // Start from the hash entry.
350     int localIndex = baseHash[baseHashIndex];
351
352     // while the index does not point to the 'Ground'
353     while (localIndex != 0) {
354       // returns the index found in case of of a matching key.
355       if (keys[localIndex] == key) {
356         return localIndex;
357       }
358
359       // next the local index
360       localIndex = next[localIndex];
361     }
362
363     // If we got this far, it could only mean we did not find the key we
364     // were asked for. return 'Ground' index.
365     return 0;
366   }
367
368   /**
369    * Find the actual index of a given key with it's baseHashIndex.<br>
370    * Some methods use the baseHashIndex. If those call {@link #find()} there's
371    * no need to re-calculate that hash.
372    * 
373    * @param key
374    * @param baseHashIndex
375    * @return the index of the given key, or 0 as 'Ground' if the key wasn't
376    *         found.
377    */
378   private int findForRemove(int key, int baseHashIndex) {
379     // Start from the hash entry.
380     this.prev = 0;
381     int index = baseHash[baseHashIndex];
382
383     // while the index does not point to the 'Ground'
384     while (index != 0) {
385       // returns the index found in case of of a matching key.
386       if (keys[index] == key) {
387         return index;
388       }
389
390       // next the local index
391       prev = index;
392       index = next[index];
393     }
394
395     // If we got this far, it could only mean we did not find the key we
396     // were asked for. return 'Ground' index.
397     this.prev = 0;
398     return 0;
399   }
400
401   /**
402    * Returns the object mapped with the given key.
403    * 
404    * @param key
405    *            int who's mapped object we're interested in.
406    * @return an object mapped by the given key. null if the key wasn't found.
407    */
408   @SuppressWarnings("unchecked")
409   public T get(int key) {
410     return (T) values[find(key)];
411   }
412
413   /**
414    * Grows the map. Allocates a new map of double the capacity, and
415    * fast-insert the old key-value pairs.
416    */
417   @SuppressWarnings("unchecked")
418   protected void grow() {
419     IntToObjectMap<T> that = new IntToObjectMap<T>(
420         this.capacity * 2);
421
422     // Iterates fast over the collection. Any valid pair is put into the new
423     // map without checking for duplicates or if there's enough space for
424     // it.
425     for (IndexIterator iterator = new IndexIterator(); iterator.hasNext();) {
426       int index = iterator.next();
427       that.prvt_put(this.keys[index], (T) this.values[index]);
428     }
429
430     // Copy that's data into this.
431     this.capacity = that.capacity;
432     this.size = that.size;
433     this.firstEmpty = that.firstEmpty;
434     this.values = that.values;
435     this.keys = that.keys;
436     this.next = that.next;
437     this.baseHash = that.baseHash;
438     this.hashFactor = that.hashFactor;
439   }
440
441   /**
442    * 
443    * @return true if the map is empty. false otherwise.
444    */
445   public boolean isEmpty() {
446     return size == 0;
447   }
448
449   /**
450    * Returns a new iterator for the mapped objects.
451    */
452   public Iterator<T> iterator() {
453     return new ValueIterator();
454   }
455
456   /** Returns an iterator on the map keys. */
457   public IntIterator keyIterator() {
458     return new KeyIterator();
459   }
460
461   /**
462    * Prints the baseHash array, used for debug purposes.
463    */
464   @SuppressWarnings("unused")
465   private void printBaseHash() {
466     for (int i = 0; i < this.baseHash.length; i++) {
467       System.out.println(i + ".\t" + baseHash[i]);
468     }
469   }
470
471   /**
472    * Inserts the &lt;key,value&gt; pair into the map. If the key already exists,
473    * this method updates the mapped value to the given one, returning the old
474    * mapped value.
475    * 
476    * @return the old mapped value, or null if the key didn't exist.
477    */
478   @SuppressWarnings("unchecked")
479   public T put(int key, T e) {
480     // Does key exists?
481     int index = find(key);
482
483     // Yes!
484     if (index != 0) {
485       // Set new data and exit.
486       T old = (T) values[index];
487       values[index] = e;
488       return old;
489     }
490
491     // Is there enough room for a new pair?
492     if (size == capacity) {
493       // No? Than grow up!
494       grow();
495     }
496
497     // Now that everything is set, the pair can be just put inside with no
498     // worries.
499     prvt_put(key, e);
500
501     return null;
502   }
503
504   /**
505    * Removes a &lt;key,value&gt; pair from the map and returns the mapped value,
506    * or null if the none existed.
507    * 
508    * @param key used to find the value to remove
509    * @return the removed value or null if none existed.
510    */
511   @SuppressWarnings("unchecked")
512   public T remove(int key) {
513     int baseHashIndex = calcBaseHashIndex(key);
514     int index = findForRemove(key, baseHashIndex);
515     if (index != 0) {
516       // If it is the first in the collision list, we should promote its
517       // next colliding element.
518       if (prev == 0) {
519         baseHash[baseHashIndex] = next[index];
520       }
521
522       next[prev] = next[index];
523       next[index] = firstEmpty;
524       firstEmpty = index;
525       --size;
526       return (T) values[index];
527     }
528
529     return null;
530   }
531
532   /**
533    * @return number of pairs currently in the map
534    */
535   public int size() {
536     return this.size;
537   }
538
539   /**
540    * Translates the mapped pairs' values into an array of Objects
541    * 
542    * @return an object array of all the values currently in the map.
543    */
544   public Object[] toArray() {
545     int j = -1;
546     Object[] array = new Object[size];
547
548     // Iterates over the values, adding them to the array.
549     for (Iterator<T> iterator = iterator(); iterator.hasNext();) {
550       array[++j] = iterator.next();
551     }
552     return array;
553   }
554
555   /**
556    * Translates the mapped pairs' values into an array of T
557    * 
558    * @param a
559    *            the array into which the elements of the list are to be
560    *            stored, if it is big enough; otherwise, use whatever space we
561    *            have, setting the one after the true data as null.
562    * 
563    * @return an array containing the elements of the list
564    * 
565    */
566   public T[] toArray(T[] a) {
567     int j = 0;
568     // Iterates over the values, adding them to the array.
569     for (Iterator<T> iterator = iterator(); j < a.length
570     && iterator.hasNext(); ++j) {
571       a[j] = iterator.next();
572     }
573
574     if (j < a.length) {
575       a[j] = null;
576     }
577
578     return a;
579   }
580
581   @Override
582   public String toString() {
583     StringBuffer sb = new StringBuffer();
584     sb.append('{');
585     IntIterator keyIterator = keyIterator();
586     while (keyIterator.hasNext()) {
587       int key = keyIterator.next();
588       sb.append(key);
589       sb.append('=');
590       sb.append(get(key));
591       if (keyIterator.hasNext()) {
592         sb.append(',');
593         sb.append(' ');
594       }
595     }
596     sb.append('}');
597     return sb.toString();
598   }
599   
600   @Override
601   public int hashCode() {
602     return getClass().hashCode() ^ size();
603   }
604   
605   @SuppressWarnings("unchecked")
606   @Override
607   public boolean equals(Object o) {
608     IntToObjectMap<T> that = (IntToObjectMap<T>)o;
609     if (that.size() != this.size()) {
610       return false;
611     }
612     
613     IntIterator it = keyIterator();
614     while (it.hasNext()) {
615       int key = it.next();
616       if (!that.containsKey(key)) {
617         return false;
618       }
619
620       T v1 = this.get(key);
621       T v2 = that.get(key);
622       if ((v1 == null && v2 != null) ||
623           (v1 != null && v2 == null) ||
624           (!v1.equals(v2))) {
625         return false;
626       }
627     }
628     return true;
629   }
630 }