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