
drv.ArrayMap.drv Maven / Gradle / Ivy
Show all versions of fastutil Show documentation
/*
* Copyright (C) 2007-2016 Sebastiano Vigna
*
* Licensed 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 PACKAGE;
import java.util.Map;
import java.util.NoSuchElementException;
import it.unimi.dsi.fastutil.objects.AbstractObjectIterator;
import it.unimi.dsi.fastutil.objects.AbstractObjectSet;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import VALUE_PACKAGE.VALUE_COLLECTION;
import VALUE_PACKAGE.VALUE_COLLECTIONS;
import VALUE_PACKAGE.VALUE_ARRAY_SET;
import VALUE_PACKAGE.VALUE_ARRAYS;
/** A simple, brute-force implementation of a map based on two parallel backing arrays.
*
* The main purpose of this
* implementation is that of wrapping cleanly the brute-force approach to the storage of a very
* small number of pairs: just put them into two parallel arrays and scan linearly to find an item.
*/
public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC implements java.io.Serializable, Cloneable {
private static final long serialVersionUID = 1L;
/** The keys (valid up to {@link #size}, excluded). */
private transient KEY_TYPE[] key;
/** The values (parallel to {@link #key}). */
private transient VALUE_TYPE[] value;
/** The number of valid entries in {@link #key} and {@link #value}. */
private int size;
/** Creates a new empty array map with given key and value backing arrays. The resulting map will have as many entries as the given arrays.
*
*
It is responsibility of the caller that the elements of key
are distinct.
*
* @param key the key array.
* @param value the value array (it must have the same length as key
).
*/
public ARRAY_MAP( final KEY_TYPE[] key, final VALUE_TYPE[] value ) {
this.key = key;
this.value = value;
size = key.length;
if( key.length != value.length ) throw new IllegalArgumentException( "Keys and values have different lengths (" + key.length + ", " + value.length + ")" );
}
/** Creates a new empty array map.
*/
public ARRAY_MAP() {
this.key = ARRAYS.EMPTY_ARRAY;
this.value = VALUE_ARRAYS.EMPTY_ARRAY;
}
/** Creates a new empty array map of given capacity.
*
* @param capacity the initial capacity.
*/
public ARRAY_MAP( final int capacity ) {
this.key = new KEY_TYPE[ capacity ];
this.value = new VALUE_TYPE[ capacity ];
}
/** Creates a new empty array map copying the entries of a given map.
*
* @param m a map.
*/
public ARRAY_MAP( final MAP KEY_VALUE_GENERIC m ) {
this( m.size() );
putAll( m );
}
/** Creates a new empty array map copying the entries of a given map.
*
* @param m a map.
*/
public ARRAY_MAP( final Map extends KEY_GENERIC_CLASS, ? extends VALUE_GENERIC_CLASS> m ) {
this( m.size() );
putAll( m );
}
/** Creates a new array map with given key and value backing arrays, using the given number of elements.
*
*
It is responsibility of the caller that the first size
elements of key
are distinct.
*
* @param key the key array.
* @param value the value array (it must have the same length as key
).
* @param size the number of valid elements in key
and value
.
*/
public ARRAY_MAP( final KEY_TYPE[] key, final VALUE_TYPE[] value, final int size ) {
this.key = key;
this.value = value;
this.size = size;
if( key.length != value.length ) throw new IllegalArgumentException( "Keys and values have different lengths (" + key.length + ", " + value.length + ")" );
if ( size > key.length ) throw new IllegalArgumentException( "The provided size (" + size + ") is larger than or equal to the backing-arrays size (" + key.length + ")" );
}
private final class EntrySet extends AbstractObjectSet implements FastEntrySet KEY_VALUE_GENERIC {
@Override
public ObjectIterator iterator() {
return new AbstractObjectIterator() {
int curr = -1, next = 0;
public boolean hasNext() {
return next < size;
}
SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
public Entry KEY_VALUE_GENERIC next() {
if ( ! hasNext() ) throw new NoSuchElementException();
return new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC( KEY_GENERIC_CAST key[ curr = next ], VALUE_GENERIC_CAST value[ next++ ] );
}
public void remove() {
if ( curr == -1 ) throw new IllegalStateException();
curr = -1;
final int tail = size-- - next--;
System.arraycopy( key, next + 1, key, next, tail );
System.arraycopy( value, next + 1, value, next, tail );
#if KEYS_REFERENCE
key[ size ] = null;
#endif
#if VALUES_REFERENCE
value[ size ] = null;
#endif
}
};
}
public ObjectIterator fastIterator() {
return new AbstractObjectIterator() {
int next = 0, curr = -1;
final BasicEntry KEY_VALUE_GENERIC entry = new BasicEntry KEY_VALUE_GENERIC ( KEY_NULL, VALUE_NULL );
public boolean hasNext() {
return next < size;
}
SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
public Entry KEY_VALUE_GENERIC next() {
if ( ! hasNext() ) throw new NoSuchElementException();
entry.key = KEY_GENERIC_CAST key[ curr = next ];
entry.value = VALUE_GENERIC_CAST value[ next++ ];
return entry;
}
public void remove() {
if ( curr == -1 ) throw new IllegalStateException();
curr = -1;
final int tail = size-- - next--;
System.arraycopy( key, next + 1, key, next, tail );
System.arraycopy( value, next + 1, value, next, tail );
#if KEYS_REFERENCE
key[ size ] = null;
#endif
#if VALUES_REFERENCE
value[ size ] = null;
#endif
}
};
}
public int size() {
return size;
}
SUPPRESS_WARNINGS_KEY_UNCHECKED
public boolean contains( Object o ) {
if ( ! ( o instanceof Map.Entry ) ) return false;
final Map.Entry,?> e = (Map.Entry,?>)o;
#if KEYS_PRIMITIVE
if (e.getKey() == null || ! (e.getKey() instanceof KEY_CLASS)) return false;
#endif
#if VALUES_PRIMITIVE
if (e.getValue() == null || ! (e.getValue() instanceof VALUE_CLASS)) return false;
#endif
final KEY_GENERIC_TYPE k = KEY_OBJ2TYPE( KEY_GENERIC_CAST e.getKey() );
return ARRAY_MAP.this.containsKey( k ) && VALUE_EQUALS( ARRAY_MAP.this.GET_VALUE( k ), VALUE_OBJ2TYPE( e.getValue() ) );
}
SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
@Override
public boolean remove( final Object o ) {
if ( !( o instanceof Map.Entry ) ) return false;
final Map.Entry,?> e = (Map.Entry,?>)o;
#if KEYS_PRIMITIVE
if (e.getKey() == null || ! (e.getKey() instanceof KEY_CLASS)) return false;
#endif
#if VALUES_PRIMITIVE
if (e.getValue() == null || ! (e.getValue() instanceof VALUE_CLASS)) return false;
#endif
final KEY_GENERIC_TYPE k = KEY_OBJ2TYPE( KEY_GENERIC_CAST e.getKey() );
final VALUE_GENERIC_TYPE v = VALUE_OBJ2TYPE( VALUE_GENERIC_CAST e.getValue() );
final int oldPos = ARRAY_MAP.this.findKey( k );
if ( oldPos == -1 || ! VALUE_EQUALS( v, ARRAY_MAP.this.value[ oldPos ] ) ) return false;
final int tail = size - oldPos - 1;
System.arraycopy( ARRAY_MAP.this.key, oldPos + 1, ARRAY_MAP.this.key, oldPos, tail );
System.arraycopy( ARRAY_MAP.this.value, oldPos + 1, ARRAY_MAP.this.value, oldPos, tail );
ARRAY_MAP.this.size--;
#if KEYS_REFERENCE
ARRAY_MAP.this.key[ size ] = null;
#endif
#if VALUES_REFERENCE
ARRAY_MAP.this.value[ size ] = null;
#endif
return true;
}
}
public FastEntrySet KEY_VALUE_GENERIC ENTRYSET() {
return new EntrySet();
}
private int findKey( final KEY_TYPE k ) {
final KEY_TYPE[] key = this.key;
for( int i = size; i-- != 0; ) if ( KEY_EQUALS( key[ i ], k ) ) return i;
return -1;
}
SUPPRESS_WARNINGS_VALUE_UNCHECKED
#if KEYS_PRIMITIVE || VALUES_PRIMITIVE
public VALUE_GENERIC_TYPE GET_VALUE( final KEY_TYPE k ) {
#else
public VALUE_GENERIC_TYPE get( final Object k ) {
#endif
final KEY_TYPE[] key = this.key;
for( int i = size; i-- != 0; ) if ( KEY_EQUALS( key[ i ], k ) ) return VALUE_GENERIC_CAST value[ i ];
return defRetValue;
}
public int size() {
return size;
}
@Override
public void clear() {
#if KEYS_REFERENCE || VALUES_REFERENCE
for( int i = size; i-- != 0; ) {
#if KEYS_REFERENCE
key[ i ] = null;
#endif
#if VALUES_REFERENCE
value[ i ] = null;
#endif
}
#endif
size = 0;
}
@Override
public boolean containsKey( final KEY_TYPE k ) {
return findKey( k ) != -1;
}
@Override
public boolean containsValue( VALUE_TYPE v ) {
for( int i = size; i-- != 0; ) if ( VALUE_EQUALS( value[ i ], v ) ) return true;
return false;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
SUPPRESS_WARNINGS_VALUE_UNCHECKED
public VALUE_GENERIC_TYPE put( KEY_GENERIC_TYPE k, VALUE_GENERIC_TYPE v ) {
final int oldKey = findKey( k );
if ( oldKey != -1 ) {
final VALUE_GENERIC_TYPE oldValue = VALUE_GENERIC_CAST value[ oldKey ];
value[ oldKey ] = v;
return oldValue;
}
if ( size == key.length ) {
final KEY_TYPE[] newKey = new KEY_TYPE[ size == 0 ? 2 : size * 2 ];
final VALUE_TYPE[] newValue = new VALUE_TYPE[ size == 0 ? 2 : size * 2 ];
for( int i = size; i-- != 0; ) {
newKey[ i ] = key[ i ];
newValue[ i ] = value[ i ];
}
key = newKey;
value = newValue;
}
key[ size ] = k;
value[ size ] = v;
size++;
return defRetValue;
}
@Override
SUPPRESS_WARNINGS_VALUE_UNCHECKED
#if KEYS_PRIMITIVE || VALUES_PRIMITIVE
public VALUE_GENERIC_TYPE REMOVE_VALUE( final KEY_TYPE k ) {
#else
public VALUE_GENERIC_TYPE remove( final Object k ) {
#endif
final int oldPos = findKey( k );
if ( oldPos == -1 ) return defRetValue;
final VALUE_GENERIC_TYPE oldValue = VALUE_GENERIC_CAST value[ oldPos ];
final int tail = size - oldPos - 1;
System.arraycopy( key, oldPos + 1, key, oldPos, tail );
System.arraycopy( value, oldPos + 1, value, oldPos, tail );
size--;
#if KEYS_REFERENCE
key[ size ] = null;
#endif
#if VALUES_REFERENCE
value[ size ] = null;
#endif
return oldValue;
}
@Override
public SET KEY_GENERIC keySet() {
return new ARRAY_SET KEY_GENERIC( key, size );
}
@Override
public VALUE_COLLECTION VALUE_GENERIC values() {
return VALUE_COLLECTIONS.unmodifiable( new VALUE_ARRAY_SET VALUE_GENERIC( value, size ) );
}
/** Returns a deep copy of this map.
*
* This method performs a deep copy of this hash map; the data stored in the
* map, however, is not cloned. Note that this makes a difference only for object keys.
*
* @return a deep copy of this map.
*/
SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
public ARRAY_MAP KEY_VALUE_GENERIC clone() {
ARRAY_MAP KEY_VALUE_GENERIC c;
try {
c = (ARRAY_MAP KEY_VALUE_GENERIC)super.clone();
}
catch(CloneNotSupportedException cantHappen) {
throw new InternalError();
}
c.key = key.clone();
c.value = value.clone();
return c;
}
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
s.defaultWriteObject();
for( int i = 0; i < size; i++ ) {
s.WRITE_KEY( key[ i ] );
s.WRITE_VALUE( value[ i ] );
}
}
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
key = new KEY_TYPE[ size ];
value = new VALUE_TYPE[ size ];
for( int i = 0; i < size; i++ ) {
key[ i ] = s.READ_KEY();
value[ i ] = s.READ_VALUE();
}
}
}