add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / facet / src / java / org / apache / lucene / util / collections / ObjectToFloatMap.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 Objects of generic type
25  * T to primitive float values.<br>
26  * The hashtable is constructed 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 ObjectToFloatMap<K> {
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     @SuppressWarnings("unchecked")
97     public void remove() {
98       ObjectToFloatMap.this.remove((K) keys[lastIndex]);
99     }
100
101   }
102
103   /**
104    * Implements an IntIterator, used for iteration over the map's keys.
105    */
106   private final class KeyIterator implements Iterator<K> {
107     private IntIterator iterator = new IndexIterator();
108
109     KeyIterator() { }
110     
111     public boolean hasNext() {
112       return iterator.hasNext();
113     }
114
115     @SuppressWarnings("unchecked")
116     public K next() {
117       return (K) keys[iterator.next()];
118     }
119
120     public void remove() {
121       iterator.remove();
122     }
123   }
124
125   /**
126    * Implements an Iterator of a generic type T used for iteration over the
127    * map's values.
128    */
129   private final class ValueIterator implements FloatIterator {
130     private IntIterator iterator = new IndexIterator();
131
132     ValueIterator() { }
133     
134     public boolean hasNext() {
135       return iterator.hasNext();
136     }
137
138     public float next() {
139       return values[iterator.next()];
140     }
141
142     public void remove() {
143       iterator.remove();
144     }
145   }
146
147   /**
148    * Default capacity - in case no capacity was specified in the constructor
149    */
150   private static int defaultCapacity = 16;
151
152   /**
153    * Holds the base hash entries. if the capacity is 2^N, than the base hash
154    * holds 2^(N+1). It can hold
155    */
156   int[] baseHash;
157
158   /**
159    * The current capacity of the map. Always 2^N and never less than 16. We
160    * never use the zero index. It is needed to improve performance and is also
161    * used as "ground".
162    */
163   private int capacity;
164   /**
165    * All objects are being allocated at map creation. Those objects are "free"
166    * or empty. Whenever a new pair comes along, a pair is being "allocated" or
167    * taken from the free-linked list. as this is just a free list.
168    */
169   private int firstEmpty;
170
171   /**
172    * hashFactor is always (2^(N+1)) - 1. Used for faster hashing.
173    */
174   private int hashFactor;
175
176   /**
177    * This array holds the unique keys
178    */
179   Object[] keys;
180
181   /**
182    * In case of collisions, we implement a double linked list of the colliding
183    * hash's with the following next[] and prev[]. Those are also used to store
184    * the "empty" list.
185    */
186   int[] next;
187
188   private int prev;
189
190   /**
191    * Number of currently objects in the map.
192    */
193   private int size;
194
195   /**
196    * This array holds the values
197    */
198   float[] values;
199
200   /**
201    * Constructs a map with default capacity.
202    */
203   public ObjectToFloatMap() {
204     this(defaultCapacity);
205   }
206
207   /**
208    * Constructs a map with given capacity. Capacity is adjusted to a native
209    * power of 2, with minimum of 16.
210    * 
211    * @param capacity
212    *            minimum capacity for the map.
213    */
214   public ObjectToFloatMap(int capacity) {
215     this.capacity = 16;
216     // Minimum capacity is 16..
217     while (this.capacity < capacity) {
218       // Multiply by 2 as long as we're still under the requested capacity
219       this.capacity <<= 1;
220     }
221
222     // As mentioned, we use the first index (0) as 'Ground', so we need the
223     // length of the arrays to be one more than the capacity
224     int arrayLength = this.capacity + 1;
225
226     this.values = new float[arrayLength];
227     this.keys = new Object[arrayLength];
228     this.next = new int[arrayLength];
229
230     // Hash entries are twice as big as the capacity.
231     int baseHashSize = this.capacity << 1;
232
233     this.baseHash = new int[baseHashSize];
234
235     // The has factor is 2^M - 1 which is used as an "AND" hashing operator.
236     // {@link #calcBaseHash()}
237     this.hashFactor = baseHashSize - 1;
238
239     this.size = 0;
240
241     clear();
242   }
243
244   /**
245    * Adds a pair to the map. Takes the first empty position from the
246    * empty-linked-list's head - {@link firstEmpty}.
247    * 
248    * New pairs are always inserted to baseHash, and are followed by the old
249    * colliding pair.
250    * 
251    * @param key
252    *            integer which maps the given Object
253    * @param e
254    *            element which is being mapped using the given key
255    */
256   private void prvt_put(K key, float e) {
257     // Hash entry to which the new pair would be inserted
258     int hashIndex = calcBaseHashIndex(key);
259
260     // 'Allocating' a pair from the "Empty" list.
261     int objectIndex = firstEmpty;
262
263     // Setting data
264     firstEmpty = next[firstEmpty];
265     values[objectIndex] = e;
266     keys[objectIndex] = key;
267
268     // Inserting the new pair as the first node in the specific hash entry
269     next[objectIndex] = baseHash[hashIndex];
270     baseHash[hashIndex] = objectIndex;
271
272     // Announcing a new pair was added!
273     ++size;
274   }
275
276   /**
277    * Calculating the baseHash index using the internal <code>hashFactor</code>.
278    * 
279    * @param key
280    */
281   protected int calcBaseHashIndex(K key) {
282     return key.hashCode() & hashFactor;
283   }
284
285   /**
286    * Empties the map. Generates the "Empty" space list for later allocation.
287    */
288   public void clear() {
289     // Clears the hash entries
290     Arrays.fill(this.baseHash, 0);
291
292     // Set size to zero
293     size = 0;
294
295     values[0] = Float.NaN;
296
297     // Mark all array entries as empty. This is done with
298     // <code>firstEmpty</code> pointing to the first valid index (1 as 0 is
299     // used as 'Ground').
300     firstEmpty = 1;
301
302     // And setting all the <code>next[i]</code> to point at
303     // <code>i+1</code>.
304     for (int i = 1; i < this.capacity;) {
305       next[i] = ++i;
306     }
307
308     // Surly, the last one should point to the 'Ground'.
309     next[this.capacity] = 0;
310   }
311
312   /**
313    * Checks if a given key exists in the map.
314    * 
315    * @param key
316    *            that is checked against the map data.
317    * @return true if the key exists in the map. false otherwise.
318    */
319   public boolean containsKey(K key) {
320     return find(key) != 0;
321   }
322
323   /**
324    * Checks if the given object exists in the map.<br>
325    * This method iterates over the collection, trying to find an equal object.
326    * 
327    * @param o
328    *            object that is checked against the map data.
329    * @return true if the object exists in the map (in .equals() meaning).
330    *         false otherwise.
331    */
332   public boolean containsValue(float o) {
333     for (FloatIterator iterator = iterator(); iterator.hasNext();) {
334       if (o == iterator.next()) {
335         return true;
336       }
337     }
338     return false;
339   }
340
341   /**
342    * Find the actual index of a given key.
343    * 
344    * @param key
345    * @return index of the key. zero if the key wasn't found.
346    */
347   protected int find(K key) {
348     // Calculate the hash entry.
349     int baseHashIndex = calcBaseHashIndex(key);
350
351     // Start from the hash entry.
352     int localIndex = baseHash[baseHashIndex];
353
354     // while the index does not point to the 'Ground'
355     while (localIndex != 0) {
356       // returns the index found in case of of a matching key.
357       if (keys[localIndex].equals(key)) {
358         return localIndex;
359       }
360
361       // next the local index
362       localIndex = next[localIndex];
363     }
364
365     // If we got this far, it could only mean we did not find the key we
366     // were asked for. return 'Ground' index.
367     return 0;
368   }
369
370   /**
371    * Find the actual index of a given key with it's baseHashIndex.<br>
372    * Some methods use the baseHashIndex. If those call {@link #find()} there's
373    * no need to re-calculate that hash.
374    * 
375    * @param key
376    * @param baseHashIndex
377    * @return the index of the given key, or 0 as 'Ground' if the key wasn't
378    *         found.
379    */
380   private int findForRemove(K key, int baseHashIndex) {
381     // Start from the hash entry.
382     this.prev = 0;
383     int index = baseHash[baseHashIndex];
384
385     // while the index does not point to the 'Ground'
386     while (index != 0) {
387       // returns the index found in case of of a matching key.
388       if (keys[index].equals(key)) {
389         return index;
390       }
391
392       // next the local index
393       prev = index;
394       index = next[index];
395     }
396
397     // If we got this far, it could only mean we did not find the key we
398     // were asked for. return 'Ground' index.
399     this.prev = 0;
400     return 0;
401   }
402
403   /**
404    * Returns the float mapped with the given key.
405    * 
406    * @param key
407    *            object who's mapped float we're interested in.
408    * @return a float mapped by the given key. Float.NaN if the key wasn't found.
409    */
410   public float get(K key) {
411     return values[find(key)];
412   }
413
414   /**
415    * Grows the map. Allocates a new map of double the capacity, and
416    * fast-insert the old key-value pairs.
417    */
418   @SuppressWarnings("unchecked")
419   protected void grow() {
420     ObjectToFloatMap<K> that = new ObjectToFloatMap<K>(
421         this.capacity * 2);
422
423     // Iterates fast over the collection. Any valid pair is put into the new
424     // map without checking for duplicates or if there's enough space for
425     // it.
426     for (IndexIterator iterator = new IndexIterator(); iterator.hasNext();) {
427       int index = iterator.next();
428       that.prvt_put((K) this.keys[index], this.values[index]);
429     }
430
431     // Copy that's data into this.
432     this.capacity = that.capacity;
433     this.size = that.size;
434     this.firstEmpty = that.firstEmpty;
435     this.values = that.values;
436     this.keys = that.keys;
437     this.next = that.next;
438     this.baseHash = that.baseHash;
439     this.hashFactor = that.hashFactor;
440   }
441
442   /**
443    * 
444    * @return true if the map is empty. false otherwise.
445    */
446   public boolean isEmpty() {
447     return size == 0;
448   }
449
450   /**
451    * Returns a new iterator for the mapped floats.
452    */
453   public FloatIterator iterator() {
454     return new ValueIterator();
455   }
456
457   /** Returns an iterator on the map keys. */
458   public Iterator<K> keyIterator() {
459     return new KeyIterator();
460   }
461
462   /**
463    * Prints the baseHash array, used for debug purposes.
464    */
465   @SuppressWarnings("unused")
466   private void printBaseHash() {
467     for (int i = 0; i < this.baseHash.length; i++) {
468       System.out.println(i + ".\t" + baseHash[i]);
469     }
470   }
471
472   /**
473    * Inserts the &lt;key,value&gt; pair into the map. If the key already exists,
474    * this method updates the mapped value to the given one, returning the old
475    * mapped value.
476    * 
477    * @return the old mapped value, or {@link Float#NaN} if the key didn't exist.
478    */
479   public float put(K key, float e) {
480     // Does key exists?
481     int index = find(key);
482
483     // Yes!
484     if (index != 0) {
485       // Set new data and exit.
486       float old = 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 Float.NaN;
502   }
503
504   /**
505    * Removes a &lt;key,value&gt; pair from the map and returns the mapped value,
506    * or {@link Float#NaN} if the none existed.
507    * 
508    * @param key used to find the value to remove
509    * @return the removed value or {@link Float#NaN} if none existed.
510    */
511   public float remove(K key) {
512     int baseHashIndex = calcBaseHashIndex(key);
513     int index = findForRemove(key, baseHashIndex);
514     if (index != 0) {
515       // If it is the first in the collision list, we should promote its
516       // next colliding element.
517       if (prev == 0) {
518         baseHash[baseHashIndex] = next[index];
519       }
520
521       next[prev] = next[index];
522       next[index] = firstEmpty;
523       firstEmpty = index;
524       --size;
525       return values[index];
526     }
527
528     return Float.NaN;
529   }
530
531   /**
532    * @return number of pairs currently in the map
533    */
534   public int size() {
535     return this.size;
536   }
537
538   /**
539    * Translates the mapped pairs' values into an array of Objects
540    * 
541    * @return an object array of all the values currently in the map.
542    */
543   public float[] toArray() {
544     int j = -1;
545     float[] array = new float[size];
546
547     // Iterates over the values, adding them to the array.
548     for (FloatIterator iterator = iterator(); iterator.hasNext();) {
549       array[++j] = iterator.next();
550     }
551     return array;
552   }
553
554   /**
555    * Translates the mapped pairs' values into an array of T
556    * 
557    * @param a
558    *            the array into which the elements of the list are to be
559    *            stored, if it is big enough; otherwise, use as much space as it can.
560    * 
561    * @return an array containing the elements of the list
562    * 
563    */
564   public float[] toArray(float[] a) {
565     int j = 0;
566     // Iterates over the values, adding them to the array.
567     for (FloatIterator iterator = iterator(); j < a.length
568     && iterator.hasNext(); ++j) {
569       a[j] = iterator.next();
570     }
571     if (j < a.length) {
572       a[j] = Float.NaN;
573     }
574
575     return a;
576   }
577
578   @Override
579   public String toString() {
580     StringBuffer sb = new StringBuffer();
581     sb.append('{');
582     Iterator<K> keyIterator = keyIterator();
583     while (keyIterator.hasNext()) {
584       K key = keyIterator.next();
585       sb.append(key);
586       sb.append('=');
587       sb.append(get(key));
588       if (keyIterator.hasNext()) {
589         sb.append(',');
590         sb.append(' ');
591       }
592     }
593     sb.append('}');
594     return sb.toString();
595   }
596   
597   @Override
598   public int hashCode() {
599     return getClass().hashCode() ^ size();
600   }
601   
602   @SuppressWarnings("unchecked")
603   @Override
604   public boolean equals(Object o) {
605     ObjectToFloatMap<K> that = (ObjectToFloatMap<K>)o;
606     if (that.size() != this.size()) {
607       return false;
608     }
609     
610     Iterator<K> it = keyIterator();
611     while (it.hasNext()) {
612       K key = it.next();
613       float v1 = this.get(key);
614       float v2 = that.get(key);
615       if (Float.compare(v1, v2) != 0) {
616         return false;
617       }
618     }
619     return true;
620   }
621 }