A space-optimized map for associating char keys with values. : Map « Collections Data Structure « Java






A space-optimized map for associating char keys with values.

      
/*
 * Copyright 2009 David Jurgens
 *
 * This file is part of the S-Space package and is covered under the terms and
 * conditions therein.
 *
 * The S-Space package is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation and distributed hereunder to you.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
 * EXPRESS OR IMPLIED ARE MADE.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
 * NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
 * WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
 * RIGHTS.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

//package edu.ucla.sspace.util;

import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;


/**
 * A space-optimized map for associating {@code char} keys with values.<p>
 *
 * This class makes a trade off for reduced space usage at the cost of decreased
 * performace.  The {@code put}, {@code get}, {@code containsKey} and {@code
 * get} operations are all logarithmic when the map is unmodified.  If a new
 * mapping is added, or one is removed, the operation is linear in the number of
 * mappings.  Both {@code size} and {@code isEmpty} are still constant time. <p>
 *
 * The {@code get} operation runs in logarithmic time.  The {@code set}
 * operation runs in consant time if setting an existing non-zero value to a
 * non-zero value.  However, if the {@code set} invocation sets a zero value to
 * non-zero, the operation is linear with the size of the array.<p>
 *
 * This map allows {@code null values}.<p>
 *
 * Iteration ordering follows the natural ordering of {@code char} values, with
 * the lowest {@code char} key value being returned first.
 *
 * <i>Implementation Note:</i> the {@code Iterator.remove()} method is currently
 * unsupported and will throw an exception when called.  However, a future
 * implementation will fix this.<p>
 *
 * @see TrieMap
 * @see IntegerMap
 * @see Map
 * 
 * @author David Jurgens
 */
public class CharMap<V> extends AbstractMap<Character,V> 
        implements java.io.Serializable {
    
    private static final long serialVersionUID = 1L;

    /**
     * The keys stored in this map, in sorted order.  The index at which a key
     * is found corresponds to the index at which its value is found.
     */
    char[] keyIndices;

    /**
     * The values stored in this map.
     */
    Object[] values;

    /**
     * Creates a new map.
     */
    public CharMap() {
  keyIndices = new char[0];
  values = new Object[0];
    }

    /**
     * Creates a new map with the mappings contained in {@code m}.
     */
    public CharMap(Map<Character,? extends V> m) {
  // Pre-allocate the arrays for all the mappings needed instead of just
  // one at time
  keyIndices = new char[m.size()];
  values = new Object[m.size()];

  // Get all of the integer keys and sort them to their correct position
  // in the key array
  Iterator<Character> it = m.keySet().iterator();
  for (int i = 0; i < m.size(); ++i) {
      keyIndices[i] = it.next();
  }
  Arrays.sort(keyIndices);

  // Then map the values to their respective positions
  for (int i = 0; i < keyIndices.length; ++i) {
      values[i] = m.get(keyIndices[i]);
  }
    }
    
    /**
     * Checks that the key is non-{@code null} and is an {@code Integer} object,
     * and then returns its {@code int} value.
     */
    private char checkKey(Object key) {
  if (key == null) {
      throw new NullPointerException("key cannot be null");
  } else if (!(key instanceof Character)) {
      throw new IllegalArgumentException("key must be an Character");
  }
  else {
      return ((Character)key).charValue();
  }
    }

    /**
     * Removes all of the mappings from this map.
     */
    public void clear() {
  keyIndices = new char[0];
  values = new Object[0];
    }

    /**
     * Returns {@code true} if this map contains a mapping for the specified key.
     */
    public boolean containsKey(Object key) {
  char k = checkKey(key);
  int index = Arrays.binarySearch(keyIndices, k);
  return index >= 0;
    }

    public boolean containsValue(Object value) {
  for (Object o : values) {
      if (o == value || (o != null && o.equals(value))) {
    return true;
      }
  }
  return false;
    }

    /**
     * Returns a {@link Set} view of the mappings contained in this map.
     */
    public Set<Entry<Character,V>> entrySet() {
  return new EntrySet();
    }

    /**
     * Returns the value to which the specified key is mapped, or {@code null}
     * if this map contains no mapping for the key.
     */  
    public V get(Object key) {
  char k = checkKey(key);
  return get(k);
    }


    /**
     * Returns the value to which the specified key is mapped, or {@code null}
     * if this map contains no mapping for the key.
     */
    @SuppressWarnings("unchecked")  
    public V get(char key) {
  int pos = Arrays.binarySearch(keyIndices, key);
  return (pos >= 0) ? (V)(values[pos]) : null;  
    }

    /**
     * Returns a {@link Set} view of the keys contained in this map.
     */
    public Set<Character> keySet() {
  return new KeySet();
    }

    /**
     * Adds the mapping from the provided key to the value.
     *
     * @param key
     * @param value
     *
     * @throws NullPointerException if the key is {@code null}
     * @throws IllegalArgumentException if the key is not an instance of {@link
     *         Integer}
     */
    public V put(Character key, V value) {
  char k = checkKey(key);
        return put(k, value);
    }

    /**
     * Adds the mapping from the provided key to the value.
     *
     * @param key
     * @param value
     *
     * @throws NullPointerException if the key is {@code null}
     * @throws IllegalArgumentException if the key is not an instance of {@link
     *         Integer}
     */
    @SuppressWarnings("unchecked")
    public V put(char key, V value) {
        char k = key;
  int index = Arrays.binarySearch(keyIndices, k);

  if (index >= 0) {
      V old = (V)(values[index]);
      values[index] = value;
      return old;
  }
  else {
      int newIndex = 0 - (index + 1);      
      Object[] newValues = Arrays.copyOf(values, values.length + 1);
      char[] newIndices = Arrays.copyOf(keyIndices, values.length + 1);

      // shift the elements down to make room for the new value
      for (int i = newIndex; i < values.length; ++i) {
    newValues[i+1] = values[i];
    newIndices[i+1] = keyIndices[i];
      }

      // insert the new value
      newValues[newIndex] = value;
      newIndices[newIndex] = k;

      // switch the arrays with the lengthed versions
      values = newValues;
      keyIndices = newIndices;
      
      return null;
  }
    }

    /**
     * Removes the mapping for a key from this map if it is present and returns
     * the value to which this map previously associated the key, or {@code
     * null} if the map contained no mapping for the key.
     *
     * @param key key whose mapping is to be removed from the map 
     *
     * @return the previous value associated with key, or {@code null} if there
     * was no mapping for key.
     */
    @SuppressWarnings("unchecked")    
    public V remove(Object key) {
  char k = checkKey(key);
        return remove(k);
    }

    /**
     * Removes the mapping for a key from this map if it is present and returns
     * the value to which this map previously associated the key, or {@code
     * null} if the map contained no mapping for the key.
     *
     * @param key key whose mapping is to be removed from the map 
     *
     * @return the previous value associated with key, or {@code null} if there
     * was no mapping for key.
     */
    @SuppressWarnings("unchecked")    
    public V remove(char key) {
        char k = key;
  int index = Arrays.binarySearch(keyIndices, k);

  if (index >= 0) {
      V old = (V)(values[index]);

      Object[] newValues = Arrays.copyOf(values, values.length - 1);
      char[] newIndices = Arrays.copyOf(keyIndices, keyIndices.length - 1);

      // shift the elements up to remove the values
      for (int i = index; i < values.length - 1; ++i) {
    newValues[i] = values[i+1];
    newIndices[i] = keyIndices[i+1];
      }

      // update the arrays with the shorted versions
      values = newValues;
      keyIndices = newIndices;
      return old;
  }

  return null;
    }

    /**
     * Returns the number of key-value mappings in this trie.
     */
    public int size() {
  return keyIndices.length;
    }

    /**
     * Returns a {@link Collection} view of the values contained in this map.
     */
    public Collection<V> values() {
  return new Values();
    }


    private class EntryIterator extends CharMapIterator<Map.Entry<Character,V>> {

  public Map.Entry<Character,V> next() {
      return nextEntry();
  }
  
    }

    private class KeyIterator extends CharMapIterator<Character> {

  public Character next() {
      return nextEntry().getKey();
  }
  
    }

    private class ValueIterator extends CharMapIterator<V> {

  public V next() {
      return nextEntry().getValue();
  }
  
    }

    abstract class CharMapIterator<E> implements Iterator<E> {

  private int next;

  public CharMapIterator() {
      next = 0;
  }

  public boolean hasNext() {
      return next < size();
  }

  @SuppressWarnings("unchecked")
  public Entry<Character,V> nextEntry() {
      if (next >= size()) {
    throw new NoSuchElementException("no further elements");
      }
      char key = keyIndices[next];
      V value = (V)(values[next]);
      next++;
      return new CharEntry(key, value);
  }
  
  // REMINDER: this class needs to work with the actual indices for the
  // key and value to avoid the logrithmic lookup
  public void remove() {
      throw new UnsupportedOperationException();
  }
    }

    // REMINDER: this class needs to work with the actual indices for the key
    // and value to avoid the logrithmic lookup
    class CharEntry extends SimpleEntry<Character,V> {

  private static final long serialVersionUID = 1L;
  
  public CharEntry(char key, V value) {
      super(key, value);
  }

  public V setValue(V newValue) {
      return CharMap.this.put(getKey(), newValue);
  }
    }

    class EntrySet extends AbstractSet<Entry<Character,V>> {

  private static final long serialVersionUID = 1L;

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

  public boolean contains(Object o) {
      if (o instanceof Map.Entry) {
    Map.Entry e = (Map.Entry)o;
    Object key = e.getKey();
    Object val = e.getValue();
    Object mapVal = CharMap.this.get(key);
    return mapVal == val || (val != null && val.equals(mapVal));
      }
      return false;
  }

  public Iterator<Map.Entry<Character,V>> iterator() {
      return new EntryIterator();
  }
  
  public int size() {
      return CharMap.this.size();
  }
    }

    class KeySet extends AbstractSet<Character> {

  private static final long serialVersionUID = 1L;
  
  public KeySet() { }
  
  public void clear() {
      CharMap.this.clear();
  }

  public boolean contains(Object o) {
      return containsKey(o);
  }

  public Iterator<Character> iterator() {
      return new KeyIterator();
  }
  
  public boolean remove(Object o) {
      return CharMap.this.remove(o) != null;
  }

  public int size() {
      return CharMap.this.size();
  }

    }

    /**
     * A {@link Collection} view of the values contained in this trie.
     */
    private class Values extends AbstractCollection<V> {

  private static final long serialVersionUID = 1L;
  
  public void clear() {
      CharMap.this.clear();
  }

  public boolean contains(Object o) {
      return containsValue(o);
  }
  
  public Iterator<V> iterator() {
      return new ValueIterator();
  }
  
  public int size() {
      return CharMap.this.size();
  }
    }
    
}
/*
 * Copyright 2009 David Jurgens
 *
 * This file is part of the S-Space package and is covered under the terms and
 * conditions therein.
 *
 * The S-Space package is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation and distributed hereunder to you.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
 * EXPRESS OR IMPLIED ARE MADE.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
 * NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
 * WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
 * RIGHTS.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */



/*
package edu.ucla.sspace.util;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.*;


// * A collection of unit tests for {@link CharMap} 
public class CharMapTests {

    
    @Test public void testConstructor() {
   CharMap<String> m = new CharMap<String>();
    }

    @Test public void testPut() {
  CharMap<String> m = new CharMap<String>();
  m.put('1', "1");
  String s = m.get('1');
  assertEquals("1", s);
    }

    @Test(expected=NullPointerException.class) 
    public void testPutNullKey() {
  CharMap<String> m = new CharMap<String>();
  m.put(null, "value");
    }

    public void testPutNullValue() {
  CharMap<String> m = new CharMap<String>();
  m.put('0', null);
  String s = m.get('0');
  assertEquals(null, s);
    }

    @Test public void testPutConflict() {
  CharMap<String> m = new CharMap<String>();
  m.put('1', "1");
  m.put('1', "2");
  String s = m.get('1');
  assertEquals("2", s);
    }

    @Test public void testProgressive() {

  CharMap<Integer> m = new CharMap<Integer>();
  assertEquals(0, m.size());
  int size = 1024;

  for (int i = 0; i < size; ++i) {
            
      m.put((char)i, Integer.valueOf(i));
      
      // check mapping
      for (Map.Entry<Character,Integer> e : m.entrySet()) {
    char p = e.getKey();
    Integer q = e.getValue();
    assertEquals(Integer.valueOf(p), q);
      }
  }
    }

    @Test public void testContainsKey() {
  CharMap<String> m = new CharMap<String>();
  m.put('1', "1");
  m.put('2', "2");

  assertTrue(m.containsKey('1'));
  assertTrue(m.containsKey('2'));
    }

    @Test public void testContainsKeyFalse() {
  CharMap<String> m = new CharMap<String>();
  m.put('1', "1");
  m.put('2', "2");

  assertFalse(m.containsKey('3'));
  assertFalse(m.containsKey('4'));
  assertFalse(m.containsKey('0'));
    }

    @Test public void testContainsValue() {
  CharMap<String> m = new CharMap<String>();
  m.put('1', "1");
  m.put('2', "2");

  assertTrue(m.containsValue("1"));
  assertTrue(m.containsValue("2"));
    }

    @Test public void testContainsValueFalse() {
  CharMap<String> m = new CharMap<String>();
  m.put('1', "1");
  m.put('2', "2");

  assertFalse(m.containsValue("3"));
  assertFalse(m.containsValue("4"));
  assertFalse(m.containsValue("5"));
    }

    @Test public void testKeySet() {
  CharMap<String> m = new CharMap<String>();

  m.put('1', "0");
  m.put('2', "1");
  m.put('3', "2");
  m.put('4', "3");
  m.put('5', "4");

  Set<Character> test = m.keySet();
  
  assertTrue(test.contains('1'));
  assertTrue(test.contains('2'));
  assertTrue(test.contains('3'));
  assertTrue(test.contains('4'));
  assertTrue(test.contains('5'));
  assertFalse(test.contains('0'));
  assertFalse(test.contains('6'));
    }

    @Test public void testKeyIterator() {
  CharMap<String> m = new CharMap<String>();
  m.put('0', "0");
  m.put('1', "1");
  m.put('2', "2");

  Set<Character> control = new HashSet<Character>();
  control.add('0');
  control.add('1');
  control.add('2');

  Set<Character> test = m.keySet();

  assertTrue(test.containsAll(control));
  assertTrue(control.containsAll(test));
    }

    @Test public void testValueIterator() {
  CharMap<String> m = new CharMap<String>();
  m.put('0', "0");
  m.put('1', "1");
  m.put('2', "2");

  Set<String> control = new HashSet<String>();
  control.add("0");
  control.add("1");
  control.add("2");

  Collection<String> test = m.values();
  
  assertEquals(control.size(), test.size());
  for (String s : test) {      
      assertTrue(control.contains(s));
  }
    }

    @Test public void testIteratorHasNext() {
  
  CharMap<String> m = new CharMap<String>();
  Iterator<Character> it = m.keySet().iterator();
  assertFalse(it.hasNext());

  m.put('0', "0");
  m.put('1', "1");
  m.put('2', "2");

  it = m.keySet().iterator();

  Set<Character> control = new HashSet<Character>();

  while (it.hasNext()) {
      control.add(it.next());
  }

  Set<Character> test = m.keySet();

  assertTrue(test.containsAll(control));
  assertTrue(control.containsAll(test));
    }

    @Test(expected=NoSuchElementException.class)
    public void testIteratorNextError() {
  
  CharMap<String> m = new CharMap<String>();
  m.put('0', "0");

  Iterator<Character> it = m.keySet().iterator();
  it.next();
  it.next(); // error
    }

    @Test(expected=NoSuchElementException.class)
    public void testEmptyIntIteratorNextError() {
  
  CharMap<String> m = new CharMap<String>();

  Iterator<Character> it = m.keySet().iterator();
  it.next(); // error
    }


    @Test public void testRemove() {
  CharMap<String> m = new CharMap<String>();
  m.put('0', "0");
  assertTrue(m.containsKey('0'));

  m.remove('0');
  assertFalse(m.containsKey('0'));
  assertEquals(0, m.size());
    }

    @Test public void testMultipleRemoves() {
  CharMap<String> m = new CharMap<String>();
  Set<Character> control = new HashSet<Character>();
  
  LinkedList<Character> list = new LinkedList<Character>();
  for (int i = 0; i < 512; ++i) {
      list.add((char)i);
  }

  for (Character c : list) {
      m.put(c, String.valueOf(c));
      control.add(c);
  }

  assertEquals(control.size(), m.size());
  assertTrue(control.containsAll(m.keySet()));

  // remove half
  int half = control.size();
  for (int i = 0; i < half; ++i) {
      Character j = list.poll();
      control.remove(j);
      m.remove(j);
  }

  assertEquals(control.size(), m.size());
  assertTrue(control.containsAll(m.keySet()));  
    }    

    @Test public void testSize() {
  CharMap<String> m = new CharMap<String>();
  assertEquals(0, m.size());
  m.put('0', "0");
  assertEquals(1, m.size());
  m.put('1', "1");
  assertEquals(2, m.size());
  m.put('2', "2");
  assertEquals(3, m.size());
    }

    @Test public void testIsEmpty() {
  CharMap<String> m = new CharMap<String>();
  assertTrue(m.isEmpty());

  m.put('0', "0");
  assertFalse(m.isEmpty());
  
  m.clear();
  assertTrue(m.isEmpty());
    }

    @Test public void testClear() {
  CharMap<String> m = new CharMap<String>();
  m.put('0', "0");
  m.put('1', "1");
  m.put('2', "2");

  m.clear();
  assertEquals(0, m.size());
  assertFalse(m.containsKey('0'));
  assertFalse(m.containsKey('1'));
  assertFalse(m.containsKey('2'));
    }

    public static void main(String args[]) {
  org.junit.runner.JUnitCore.main(CharMapTests.class.getName());
    }

}
*/

   
    
    
    
    
    
  








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 Map implementation that grows to a fixed size and then retains only a fixed number of the highest (largest) keys.
34.Class which creates mapping between keys and a list of values.
35.A map of values by class.
36.History Map
37.Sorts map by values in ascending order.
38.Map from a given key to a list of values
39.Map from a given key to a set of values
40.Class which keeps a set of values and assigns each value a unique positive index.
41.Array Map
42.Array map
43.An ArrayMap is a very inefficient map type that is more robust in dealing with changes to its keys than other maps.
44.This Map stores it's keys as strings in upper case, null and duplicate keys are not allowed
45.Map to string
46.A simple class that stores key Strings as char[]'s in a hash table.