A simple class that stores key Strings as char[]'s in a hash table. : Map « Collections Data Structure « Java






A simple class that stores key Strings as char[]'s in a hash table.

     
/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//package org.apache.solr.util;

import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * A simple class that stores key Strings as char[]'s in a hash table. Note that
 * this is not a general purpose class. For example, it cannot remove items from
 * the map, nor does it resize its hash table to be smaller, etc. It is designed
 * to be quick to retrieve items by char[] keys without the necessity of
 * converting to a String first.
 */

public class CharArrayMap<V> extends AbstractMap<String, V> implements
    Map<String, V>, Cloneable, Serializable {
  private final static int INIT_SIZE = 2;
  private char[][] keys;
  private Object[] values;
  private int count;
  private final boolean ignoreCase;

  /**
   * Create map with enough capacity to hold startSize terms
   */
  public CharArrayMap(int initialCapacity, boolean ignoreCase) {
    this.ignoreCase = ignoreCase;
    int size = INIT_SIZE;
    // load factor of .75, inverse is 1.25, or x+x/4
    initialCapacity = initialCapacity + (initialCapacity >> 2);
    while (size <= initialCapacity)
      size <<= 1;
    keys = new char[size][];
    values = new Object[size];
  }

  public boolean ignoreCase() {
    return ignoreCase;
  }

  public V get(char[] key) {
    return get(key, 0, key.length);
  }

  public V get(char[] key, int off, int len) {
    return (V) values[getSlot(key, off, len)];
  }

  public V get(CharSequence key) {
    return (V) values[getSlot(key)];
  }

  @Override
  public V get(Object key) {
    return (V) values[getSlot(key)];
  }

  @Override
  public boolean containsKey(Object s) {
    return keys[getSlot(s)] != null;
  }

  @Override
  public boolean containsValue(Object value) {
    if (value == null) {
      // search for key with a null value
      for (int i = 0; i < keys.length; i++) {
        if (keys[i] != null && values[i] == null)
          return true;
      }
      return false;
    }

    for (int i = 0; i < values.length; i++) {
      Object val = values[i];
      if (val != null && value.equals(val))
        return true;
    }
    return false;
  }

  private int getSlot(Object key) {
    if (key instanceof char[]) {
      char[] keyc = (char[]) key;
      return getSlot(keyc, 0, keyc.length);
    }
    return getSlot((CharSequence) key);
  }

  private int getSlot(char[] key, int off, int len) {
    int code = getHashCode(key, len);
    int pos = code & (keys.length - 1);
    char[] key2 = keys[pos];
    if (key2 != null && !equals(key, off, len, key2)) {
      final int inc = ((code >> 8) + code) | 1;
      do {
        code += inc;
        pos = code & (keys.length - 1);
        key2 = keys[pos];
      } while (key2 != null && !equals(key, off, len, key2));
    }
    return pos;
  }

  /** Returns true if the String is in the set */
  private int getSlot(CharSequence key) {
    int code = getHashCode(key);
    int pos = code & (keys.length - 1);
    char[] key2 = keys[pos];
    if (key2 != null && !equals(key, key2)) {
      final int inc = ((code >> 8) + code) | 1;
      do {
        code += inc;
        pos = code & (keys.length - 1);
        key2 = keys[pos];
      } while (key2 != null && !equals(key, key2));
    }
    return pos;
  }

  public V put(CharSequence key, V val) {
    return put(key.toString(), val); // could be more efficient
  }

  @Override
  public V put(String key, V val) {
    return put(key.toCharArray(), val);
  }

  /**
   * Add this key,val pair to the map. The char[] key is directly used, no
   * copy is made. If ignoreCase is true for this Map, the key array will be
   * directly modified. The user should never modify the key after calling
   * this method.
   */
  public V put(char[] key, Object val) {
    if (ignoreCase)
      for (int i = 0; i < key.length; i++)
        key[i] = Character.toLowerCase(key[i]);
    int slot = getSlot(key, 0, key.length);
    if (keys[slot] == null)
      count++;
    Object prev = values[slot];
    keys[slot] = key;
    values[slot] = val;

    if (count + (count >> 2) >= keys.length) {
      rehash();
    }

    return (V) prev;
  }

  private boolean equals(char[] text1, int off, int len, char[] text2) {
    if (len != text2.length)
      return false;
    if (ignoreCase) {
      for (int i = 0; i < len; i++) {
        if (Character.toLowerCase(text1[off + i]) != text2[i])
          return false;
      }
    } else {
      for (int i = 0; i < len; i++) {
        if (text1[off + i] != text2[i])
          return false;
      }
    }
    return true;
  }

  private boolean equals(CharSequence text1, char[] text2) {
    int len = text1.length();
    if (len != text2.length)
      return false;
    if (ignoreCase) {
      for (int i = 0; i < len; i++) {
        if (Character.toLowerCase(text1.charAt(i)) != text2[i])
          return false;
      }
    } else {
      for (int i = 0; i < len; i++) {
        if (text1.charAt(i) != text2[i])
          return false;
      }
    }
    return true;
  }

  private void rehash() {
    final int newSize = 2 * keys.length;
    char[][] oldEntries = keys;
    Object[] oldValues = values;
    keys = new char[newSize][];
    values = new Object[newSize];

    for (int i = 0; i < oldEntries.length; i++) {
      char[] key = oldEntries[i];
      if (key != null) {
        // todo: could be faster... no need to compare keys on collision
        // since they are unique
        int newSlot = getSlot(key, 0, key.length);
        keys[newSlot] = key;
        values[newSlot] = oldValues[i];
      }
    }
  }

  private int getHashCode(char[] text, int len) {
    int code = 0;
    if (ignoreCase) {
      for (int i = 0; i < len; i++) {
        code = code * 31 + Character.toLowerCase(text[i]);
      }
    } else {
      for (int i = 0; i < len; i++) {
        code = code * 31 + text[i];
      }
    }
    return code;
  }

  private int getHashCode(CharSequence text) {
    int code;
    if (ignoreCase) {
      code = 0;
      int len = text.length();
      for (int i = 0; i < len; i++) {
        code = code * 31 + Character.toLowerCase(text.charAt(i));
      }
    } else {
      if (false && text instanceof String) {
        code = text.hashCode();
      } else {
        code = 0;
        int len = text.length();
        for (int i = 0; i < len; i++) {
          code = code * 31 + text.charAt(i);
        }
      }
    }
    return code;
  }

  @Override
  public int size() {
    return count;
  }

  @Override
  public boolean isEmpty() {
    return count == 0;
  }

  @Override
  public void clear() {
    count = 0;
    Arrays.fill(keys, null);
    Arrays.fill(values, null);
  }

  @Override
  public Set<Entry<String, V>> entrySet() {
    return new EntrySet();
  }

  /** Returns an EntryIterator over this Map. */
  public EntryIterator iterator() {
    return new EntryIterator();
  }

  /** public iterator class so efficient methods are exposed to users */
  public class EntryIterator implements Iterator<Map.Entry<String, V>> {
    int pos = -1;
    int lastPos;

    EntryIterator() {
      goNext();
    }

    private void goNext() {
      lastPos = pos;
      pos++;
      while (pos < keys.length && keys[pos] == null)
        pos++;
    }

    public boolean hasNext() {
      return pos < keys.length;
    }

    /** gets the next key... do not modify the returned char[] */
    public char[] nextKey() {
      goNext();
      return keys[lastPos];
    }

    /** gets the next key as a newly created String object */
    public String nextKeyString() {
      return new String(nextKey());
    }

    /** returns the value associated with the last key returned */
    public V currentValue() {
      return (V) values[lastPos];
    }

    /** sets the value associated with the last key returned */
    public V setValue(V value) {
      V old = (V) values[lastPos];
      values[lastPos] = value;
      return old;
    }

    /**
     * Returns an Entry<String,V> object created on the fly... use
     * nextCharArray() + currentValie() for better efficiency.
     */
    public Map.Entry<String, V> next() {
      goNext();
      return new MapEntry(lastPos);
    }

    public void remove() {
      throw new UnsupportedOperationException();
    }
  }

  private class MapEntry implements Map.Entry<String, V> {
    final int pos;

    MapEntry(int pos) {
      this.pos = pos;
    }

    public char[] getCharArr() {
      return keys[pos];
    }

    public String getKey() {
      return new String(getCharArr());
    }

    public V getValue() {
      return (V) values[pos];
    }

    public V setValue(V value) {
      V old = (V) values[pos];
      values[pos] = value;
      return old;
    }

    public String toString() {
      return getKey() + '=' + getValue();
    }
  }

  private class EntrySet extends AbstractSet<Map.Entry<String, V>> {
    public EntryIterator iterator() {
      return new EntryIterator();
    }

    public boolean contains(Object o) {
      if (!(o instanceof Map.Entry))
        return false;
      Map.Entry e = (Map.Entry) o;
      Object key = e.getKey();
      if (key == null)
        return false; // we don't support null keys
      Object val = e.getValue();
      Object v = get(key);
      return v == null ? val == null : v.equals(val);
    }

    public boolean remove(Object o) {
      throw new UnsupportedOperationException();
    }

    public int size() {
      return count;
    }

    public void clear() {
      CharArrayMap.this.clear();
    }
  }

  @Override
  public Object clone() {
    CharArrayMap<V> map = null;
    try {
      map = (CharArrayMap<V>) super.clone();
      map.keys = keys.clone();
      map.values = values.clone();
    } catch (CloneNotSupportedException e) {
      // impossible
    }
    return map;
  }
}

   
    
    
    
    
  








Related examples in the same category

1.Creating and storing arrays in a map
2.Sort based on the values
3.Get a key from value with an HashMap
4.Retrieve environment variables (JDK1.5)
5.Creating a Type-Specific Map: creates a map whose keys are Integer objects and values are String objects.
6.A map declared to hold objects of a type T can also hold objects that extend from T
7.A value retrieved from a type-specific collection does not need to be casted
8.Map techniques.
9.Create an array containing the keys in a map
10.Create an array containing the values in a map
11.Creating a Hash Table
12.Creating a Map That Retains Order-of-Insertion
13.Automatically Removing an Unreferenced Element from a Hash Table
14.Creating a Type-Specific Map [5.0]
15.Use Iterator to loop through the HashMap class
16.Create type specific collections
17.Convert Properties into Map
18.A java.util.Map implementation using reference values
19.Utility method that return a String representation of a map. The elements will be represented as "key = value"
20.Utility method that return a String representation of a map. The elements will be represented as "key = value" (tab)
21.This program demonstrates the use of a map with key type String and value type Employee
22.Format a Map
23.A Map implementation that dumps its content when memory runs low.
24.A Map that stores the values in files within a directory.
25.Map List
26.Multi Value Map Array List
27.Multi Value Map Linked HashSet
28.An object that maps keys to values, and values back to keys.
29.LRU Map
30.A map acts like array.
31.Order Retaining Map
32.BinaryMap class implements a map from objects to integer objects where the only value is the integer with value 1.
33.A space-optimized map for associating char keys with values.
34.A Map implementation that grows to a fixed size and then retains only a fixed number of the highest (largest) keys.
35.Class which creates mapping between keys and a list of values.
36.A map of values by class.
37.History Map
38.Sorts map by values in ascending order.
39.Map from a given key to a list of values
40.Map from a given key to a set of values
41.Class which keeps a set of values and assigns each value a unique positive index.
42.Array Map
43.Array map
44.An ArrayMap is a very inefficient map type that is more robust in dealing with changes to its keys than other maps.
45.This Map stores it's keys as strings in upper case, null and duplicate keys are not allowed
46.Map to string