alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.core.internal.utils;

import java.util.*;

/**
 * A specialized map implementation that is optimized for a 
 * small set of object keys. 
 * 
 * Implemented as a single array that alternates keys and values.
 */
public class ObjectMap implements Map, IStringPoolParticipant {

	// 8 attribute keys, 8 attribute values
	protected static final int DEFAULT_SIZE = 16;
	protected static final int GROW_SIZE = 10;
	protected int count = 0;
	protected Object[] elements = null;

	/**
	 * Creates a new object map of default size
	 */
	public ObjectMap() {
		this(DEFAULT_SIZE);
	}

	/**
	 * Creates a new object map.
	 * @param initialCapacity The initial number of elements that will fit in the map.
	 */
	public ObjectMap(int initialCapacity) {
		if (initialCapacity > 0)
			elements = new Object[Math.max(initialCapacity * 2, 0)];
	}

	/**
	 * Creates a new object map of the same size as the given map and 
	 * populate it with the key/attribute pairs found in the map.
	 * @param map The entries in the given map will be added to the new map.
	 */
	public ObjectMap(Map map) {
		this(map.size());
		putAll(map);
	}

	/**
	 * @see Map#clear()
	 */
	public void clear() {
		elements = null;
		count = 0;
	}

	/**
	 * @see java.lang.Object#clone()
	 */
	public Object clone() {
		return new ObjectMap(this);
	}

	/**
	 * @see Map#containsKey(java.lang.Object)
	 */
	public boolean containsKey(Object key) {
		if (elements == null || count == 0)
			return false;
		for (int i = 0; i < elements.length; i = i + 2)
			if (elements[i] != null && elements[i].equals(key))
				return true;
		return false;
	}

	/**
	 * @see Map#containsValue(java.lang.Object)
	 */
	public boolean containsValue(Object value) {
		if (elements == null || count == 0)
			return false;
		for (int i = 1; i < elements.length; i = i + 2)
			if (elements[i] != null && elements[i].equals(value))
				return true;
		return false;
	}

	/**
	 * @see Map#entrySet()
	 * This implementation does not conform properly to the specification
	 * in the Map interface.  The returned collection will not be bound to
	 * this map and will not remain in sync with this map.
	 */
	public Set entrySet() {
		return count == 0 ? Collections.EMPTY_SET : toHashMap().entrySet();
	}

	/**
	 * See Object#equals
	 */
	public boolean equals(Object o) {
		if (!(o instanceof Map))
			return false;
		Map other = (Map) o;
		//must be same size
		if (count != other.size())
			return false;

		//keysets must be equal
		if (!keySet().equals(other.keySet()))
			return false;

		//values for each key must be equal
		for (int i = 0; i < elements.length; i = i + 2) {
			if (elements[i] != null && (!elements[i + 1].equals(other.get(elements[i]))))
				return false;
		}
		return true;
	}

	/**
	 * @see Map#get(java.lang.Object)
	 */
	public Object get(Object key) {
		if (elements == null || count == 0)
			return null;
		for (int i = 0; i < elements.length; i = i + 2)
			if (elements[i] != null && elements[i].equals(key))
				return elements[i + 1];
		return null;
	}

	/**
	 * The capacity of the map has been exceeded, grow the array by
	 * GROW_SIZE to accommodate more entries.
	 */
	protected void grow() {
		Object[] expanded = new Object[elements.length + GROW_SIZE];
		System.arraycopy(elements, 0, expanded, 0, elements.length);
		elements = expanded;
	}

	/**
	 * See Object#hashCode
	 */
	public int hashCode() {
		int hash = 0;
		for (int i = 0; i < elements.length; i = i + 2) {
			if (elements[i] != null) {
				hash += elements[i].hashCode();
			}
		}
		return hash;
	}

	/**
	 * @see Map#isEmpty()
	 */
	public boolean isEmpty() {
		return count == 0;
	}

	/**
	 * @see Map#keySet()
	 * This implementation does not conform properly to the specification
	 * in the Map interface.  The returned collection will not be bound to
	 * this map and will not remain in sync with this map.
	 */
	public Set keySet() {
		Set result = new HashSet(size());
		for (int i = 0; i < elements.length; i = i + 2) {
			if (elements[i] != null) {
				result.add(elements[i]);
			}
		}
		return result;
	}

	/**
	 * @see Map#put(java.lang.Object, java.lang.Object)
	 */
	public Object put(Object key, Object value) {
		if (key == null)
			throw new NullPointerException();
		if (value == null)
			return remove(key);

		// handle the case where we don't have any attributes yet
		if (elements == null)
			elements = new Object[DEFAULT_SIZE];
		if (count == 0) {
			elements[0] = key;
			elements[1] = value;
			count++;
			return null;
		}

		int emptyIndex = -1;
		// replace existing value if it exists
		for (int i = 0; i < elements.length; i += 2) {
			if (elements[i] != null) {
				if (elements[i].equals(key)) {
					Object oldValue = elements[i + 1];
					elements[i + 1] = value;
					return oldValue;
				}
			} else if (emptyIndex == -1) {
				// keep track of the first empty index
				emptyIndex = i;
			}
		}
		// this will put the emptyIndex greater than the size but
		// that's ok because we will grow first.
		if (emptyIndex == -1)
			emptyIndex = count * 2;

		// otherwise add it to the list of elements.
		// grow if necessary
		if (elements.length <= (count * 2))
			grow();
		elements[emptyIndex] = key;
		elements[emptyIndex + 1] = value;
		count++;
		return null;
	}

	/**
	 * @see Map#putAll(java.util.Map)
	 */
	public void putAll(Map map) {
		for (Iterator i = map.keySet().iterator(); i.hasNext();) {
			Object key = i.next();
			Object value = map.get(key);
			put(key, value);
		}
	}

	/**
	 * @see Map#remove(java.lang.Object)
	 */
	public Object remove(Object key) {
		if (elements == null || count == 0)
			return null;
		for (int i = 0; i < elements.length; i = i + 2) {
			if (elements[i] != null && elements[i].equals(key)) {
				elements[i] = null;
				Object result = elements[i + 1];
				elements[i + 1] = null;
				count--;
				return result;
			}
		}
		return null;
	}

	/**
	 * @see Map#size()
	 */
	public int size() {
		return count;
	}

	/* (non-Javadoc
	 * Method declared on IStringPoolParticipant
	 */
	public void shareStrings(StringPool set) {
		//copy elements for thread safety
		Object[] array = elements;
		if (array == null)
			return;
		for (int i = 0; i < array.length; i++) {
			Object o = array[i];
			if (o instanceof String)
				array[i] = set.add((String) o);
			if (o instanceof IStringPoolParticipant)
				((IStringPoolParticipant) o).shareStrings(set);
		}
	}

	/**
	 * Creates a new hash map with the same contents as this map.
	 */
	private HashMap toHashMap() {
		HashMap result = new HashMap(size());
		for (int i = 0; i < elements.length; i = i + 2) {
			if (elements[i] != null) {
				result.put(elements[i], elements[i + 1]);
			}
		}
		return result;
	}

	/**
	 * @see Map#values()
	 * This implementation does not conform properly to the specification
	 * in the Map interface.  The returned collection will not be bound to
	 * this map and will not remain in sync with this map.
	 */
	public Collection values() {
		Set result = new HashSet(size());
		for (int i = 1; i < elements.length; i = i + 2) {
			if (elements[i] != null) {
				result.add(elements[i]);
			}
		}
		return result;
	}
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.