org.apache.myfaces.trinidad.util.ArrayMap Maven / Gradle / Ivy
Show all versions of trinidad-api Show documentation
/*
* 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.myfaces.trinidad.util;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A Map implementation that stores its contents in a single
* array. This approach is significantly faster for small sets of
* data than the use of a HashMap or Hashtable, though potentially
* much slower for very large sets.
*
* ArrayMap is optimized for many-reads-few-write. In particular,
* it reallocates its array on any insertion or deletion.
*
* ArrayMap also includes a series of static methods for managing the
* Object array. These may be used in place of instantiating an
* ArrayMap for clients that don't need a Map implementation.
* Clients using these methods must be careful to store the returned
* Object array on any mutator method. They also must provide their
* own synchronization, if needed. When using these static methods,
* clients can opt to search for objects by identity (via
* getByIdentity()
) instead of equality, while the static
* get()
method will try identity before equality. This
* latter approach is extremely fast but still safe for retrieval of
* Strings that have all been interned, especially if misses are
* infrequent (since misses do require a scan using Object.equals()).
* It's worth remembering that String constants are always interned,
* as required by the language specification.
*
* @version $Name: $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/util/ArrayMap.java#0 $) $Date: 10-nov-2005.19:08:36 $
*/
// -= Simon Lessard =-
// Using a single array for both the key and the value leads to many
// problems, especially with type safety. Using parallel arrays or
// a single array containing nodes would be a much cleaner/safer idea.
// =-= AdamWiner =-=
// True, but the whole point of this class is maximal efficiency for
// small, transient arrays. The type safety problems are entirely internal,
// not exposed to clients. Parallel arrays or arrays containing nodes
// would be less efficient - if you're willing to allocate bonus objects,
// and don't care about efficiency, just use HashMap.
public class ArrayMap extends AbstractMap implements Cloneable
{
/**
* Creates an empty ArrayMap, preallocating nothing.
*/
public ArrayMap()
{
this(0, 1);
}
/**
* Creates an ArrayMap, preallocating for a certain size.
* @param size the number of elements to pre-allocate for
*/
public ArrayMap(int size)
{
this(size, 1);
}
/**
* Creates an ArrayMap, preallocating for a certain size.
* @param size the number of elements to pre-allocate for
* @param increment the number of additional elements to
* allocate for when overruning
*/
public ArrayMap(int size, int increment)
{ if ((increment < 1) || (size < 0))
throw new IllegalArgumentException();
if (size > 0)
_array = new Object[2 * size];
_increment = increment;
}
/**
* Returns the key at a specific index in the map.
*/
@SuppressWarnings("unchecked")
public K getKey(int index)
{
if ((index < 0) || (index >= size()))
throw new IndexOutOfBoundsException();
return (K) _array[index * 2];
}
/**
* Returns the value at a specific index in the map.
*/
@SuppressWarnings("unchecked")
public V getValue(int index)
{
if ((index < 0) || (index >= size()))
throw new IndexOutOfBoundsException();
return (V) _array[index * 2 + 1];
}
/**
* Gets the object stored with the given key. Scans first
* by object identity, then by object equality.
*/
static public Object get(Object[] array, Object key)
{
Object o = getByIdentity(array, key);
if (o != null)
return o;
return getByEquality(array, key);
}
/**
* Gets the object stored with the given key, using
* only object identity.
*/
static public Object getByIdentity(Object[] array, Object key)
{
if (array != null)
{
int length = array.length;
for (int i = 0; i < length; i += 2)
{
if (array[i] == key)
return array[i + 1];
}
}
return null;
}
/**
* Gets the object stored with the given key, using
* only object equality.
*/
static public Object getByEquality(Object[] array, Object key)
{
if (array != null)
{
int length = array.length;
for (int i = 0; i < length; i += 2)
{
Object targetKey = array[i];
if (targetKey == null)
return null;
else if (targetKey.equals(key))
return array[i + 1];
}
}
return null;
}
/**
* Adds the key/value pair to the array, returning a
* new array if necessary.
*/
static public Object[] put(Object[] array, Object key, Object value)
{
if (array != null)
{
int length = array.length;
for (int i = 0; i < length; i+=2)
{
Object curKey = array[i];
if (((curKey != null) && (curKey.equals(key))) || (curKey == key))
{
array[i + 1] = value;
return array;
}
}
}
return _addToArray(array, key, value, 1);
}
/**
* Removes the value for the key from the array, returning a
* new array if necessary.
*/
static public Object[] remove(Object[] array, Object key)
{
return remove(array, key, true);
}
/**
* Removes the value for the key from the array, returning a
* new array if necessary.
*/
static public Object[] remove(Object[] array, Object key,
boolean reallocate)
{
if (array != null)
{
int length = array.length;
for (int i = 0; i < length; i+=2)
{
Object curKey = array[i];
if (((curKey != null) && curKey.equals(key)) || (curKey == key))
{
Object[] newArray = array;
if (reallocate)
{
newArray = new Object[length - 2];
System.arraycopy(array, 0, newArray, 0, i);
}
System.arraycopy(array, i + 2, newArray, i, length - i - 2);
if (!reallocate)
{
array[length - 1] = null;
array[length - 2] = null;
}
return newArray;
}
}
}
return array;
}
//
// GENERIC MAP API
//
@Override
public int size()
{
return _size;
}
@Override
public boolean containsValue(Object value)
{
int entryCount = size() * 2;
for (int i = 0; i < entryCount; i += 2)
{
if (_equals(value, _array[i + 1]))
return true;
}
return false;
}
@Override
public boolean containsKey(Object key)
{
int entryCount = size() * 2;
for (int i = 0; i < entryCount; i += 2)
{
if (_equals(key, _array[i]))
return true;
}
return false;
}
/**
* Returns an enumeration of the keys in this map.
* the Iterator methods on the returned object to fetch the elements
* sequentially.
*/
@SuppressWarnings("unchecked")
public Iterator keys()
{
int size = _size;
if (size == 0)
return null;
ArrayList keyList = new ArrayList();
int i = (size-1)*2;
while(i>=0)
{
keyList.add((K) _array[i]);
i=i-2;
}
return keyList.iterator();
}
/**
* Returns an Iterator of keys in the array.
*/
public static Iterator