All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.core.internal.preferences.ImmutableMap Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2005, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.core.internal.preferences;


/**
 * Hash table of {String --> String}.
 *
 * This map handles collisions using linear probing.  When elements are
 * removed, the entire table is rehashed.  Thus this map has good space
 * characteristics, good insertion and iteration performance, but slower
 * removal performance than a HashMap.
 * 

* This map is thread safe because it is immutable. All methods that modify * the map create and return a new map, rather than modifying the receiver. */ public abstract class ImmutableMap implements Cloneable { static class ArrayMap extends ImmutableMap { private static final float LOAD_FACTOR = 0.45f; /** * number of elements in the table */ private int elementSize; /** * The table keys */ private String[] keyTable; private int threshold; private String[] valueTable; ArrayMap(int size) { this.elementSize = 0; //table size must always be a power of two int tableLen = 1; while (tableLen < size) tableLen *= 2; this.keyTable = new String[tableLen]; this.valueTable = new String[tableLen]; this.threshold = (int) (tableLen * LOAD_FACTOR); } @Override public String get(String key) { int lengthMask = keyTable.length - 1; int index = key.hashCode() & lengthMask; String currentKey; while ((currentKey = keyTable[index]) != null) { if (currentKey.equals(key)) return valueTable[index]; index = (index + 1) & lengthMask; } return null; } /** * This method destructively adds the key/value pair to the table. * The caller must ensure the table has an empty slot before calling * this method. * @param key * @param value */ @Override protected void internalPut(String key, String value) { int lengthMask = keyTable.length - 1; int index = key.hashCode() & lengthMask; String currentKey; while ((currentKey = keyTable[index]) != null) { if (currentKey.equals(key)) { valueTable[index] = value; return; } index = (index + 1) & lengthMask; } keyTable[index] = key; valueTable[index] = value; ++elementSize; } /** * Returns an array of all keys in this map. */ @Override public String[] keys() { if (elementSize == 0) return EMPTY_STRING_ARRAY; String[] result = new String[elementSize]; int next = 0; for (String key : keyTable) { if (key != null) { result[next++] = key; } } return result; } @Override public ImmutableMap put(String key, String value) { ArrayMap result; final int oldLen = keyTable.length; if (elementSize + 1 > threshold) { //rehash case String currentKey; result = new ArrayMap(oldLen * 2); for (int i = oldLen; --i >= 0;) if ((currentKey = keyTable[i]) != null) result.internalPut(currentKey, valueTable[i]); } else { result = new ArrayMap(oldLen); System.arraycopy(this.keyTable, 0, result.keyTable, 0, this.keyTable.length); System.arraycopy(this.valueTable, 0, result.valueTable, 0, this.valueTable.length); result.elementSize = this.elementSize; } result.internalPut(key, value); return result; } @Override public ImmutableMap removeKey(String key) { final int lengthMask = keyTable.length - 1; int index = key.hashCode() & lengthMask; String currentKey; while ((currentKey = keyTable[index]) != null) { if (currentKey.equals(key)) { if (elementSize <= 1) return EMPTY; //return a new map that includes all keys except the current one ImmutableMap result = createMap((int) (elementSize / LOAD_FACTOR)); for (int i = 0; i < index; i++) if ((currentKey = keyTable[i]) != null) result.internalPut(currentKey, valueTable[i]); for (int i = index + 1; i <= lengthMask; i++) if ((currentKey = keyTable[i]) != null) result.internalPut(currentKey, valueTable[i]); return result; } index = (index + 1) & lengthMask; } return this; } @Override public void shareStrings(StringPool set) { //copy elements for thread safety String[] array = keyTable; if (array == null) return; for (int i = 0; i < array.length; i++) { String o = array[i]; if (o != null) array[i] = set.add(o); } array = valueTable; if (array == null) return; for (int i = 0; i < array.length; i++) { String o = array[i]; if (o != null) array[i] = set.add(o); } } @Override public int size() { return elementSize; } } static class EmptyMap extends ImmutableMap { @Override public String get(String value) { return null; } @Override public ImmutableMap removeKey(String key) { return this; } @Override protected void internalPut(String key, String value) { throw new IllegalStateException();//cannot put elements in the empty map } @Override public String[] keys() { return EMPTY_STRING_ARRAY; } @Override public ImmutableMap put(String key, String value) { ImmutableMap result = createMap(4); result.internalPut(key, value); return result; } @Override public int size() { return 0; } } /** * The empty hash map. Since instances are immutable, the empty map * can be a singleton, with accessor methods optimized for the empty map case. */ public static final ImmutableMap EMPTY = new EmptyMap(); protected static final String[] EMPTY_STRING_ARRAY = new String[0]; /** * Returns the value associated with this key in the map, or * null if the key is not present in the map. * @param key * @return The value associated with this key, or null */ public abstract String get(String key); protected static ImmutableMap createMap(int i) { if (i <= 0) return EMPTY; return new ArrayMap(i); } /** * Destructively adds a key/value pair to this map. The caller must ensure * there is enough room in this map to proceed. * * @param key * @param value */ protected abstract void internalPut(String key, String value); /** * Returns an array of all keys in this map. */ public abstract String[] keys(); /** * Returns a new map that is equal to this one, except with the given * key/value pair added. * * @param key * @param value * @return The map containing the given key/value pair */ public abstract ImmutableMap put(String key, String value); /** * Returns a map that is equal to this one, except without the given * key. * @param key * @return A map with the given key removed */ public abstract ImmutableMap removeKey(String key); public void shareStrings(StringPool set) { // nothing to do } /** * Returns the number of keys in this map. * @return the number of keys in this map. */ public abstract int size(); @Override public String toString() { StringBuilder s = new StringBuilder(); for (String key : keys()) s.append(key).append(" -> ").append(get(key)).append("\n"); //$NON-NLS-2$ //$NON-NLS-1$ return s.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy