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

java.util.LinkedHashMap Maven / Gradle / Ivy

There is a newer version: 0.96-beta4
Show newest version
/*
 * Copyright 2008 Google Inc.
 * 
 * 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 java.util;

/**
 * Hash table implementation of the Map interface with predictable iteration
 * order. [Sun
 * docs]
 * 
 * @param  key type.
 * @param  value type.
 */
public class LinkedHashMap extends HashMap implements Map
{

	/**
	 * The entry we use includes next/prev pointers for a doubly-linked circular
	 * list with a head node. This reduces the special cases we have to deal with
	 * in the list operations.
	 * 
	 * Note that we duplicate the key from the underlying hash map so we can find
	 * the eldest entry. The alternative would have been to modify HashMap so more
	 * of the code was directly usable here, but this would have added some
	 * overhead to HashMap, or to reimplement most of the HashMap code here with
	 * small modifications. Paying a small storage cost only if you use
	 * LinkedHashMap and minimizing code size seemed like a better tradeoff
	 */
	private class ChainEntry extends MapEntryImpl
	{
		private transient ChainEntry next;
		private transient ChainEntry prev;

		public ChainEntry()
		{
			this(null, null);
		}

		public ChainEntry(K key, V value)
		{
			super(key, value);
			next= prev= null;
		}

		/**
		 * Add this node to the end of the chain.
		 */
		public void addToEnd()
		{
			ChainEntry tail= head.prev;

			// Chain is valid.
			assert (head != null && tail != null);

			// This entry is not in the list.
			assert (next == null) && (prev == null);

			// Update me.
			prev= tail;
			next= head;
			tail.next= head.prev= this;
		}

		/**
		 * Remove this node from any list it may be a part of.
		 */
		public void remove()
		{
			next.prev= prev;
			prev.next= next;
			next= prev= null;
		}
	}

	private final class EntrySet extends HashSet>
	{
		private final class EntryIterator implements Iterator>
		{
			// The last entry that was returned from this iterator.
			private ChainEntry last;

			// The next entry to return from this iterator.
			private ChainEntry next;

			public EntryIterator()
			{
				next= head.next;
			}

			public boolean hasNext()
			{
				return next != head;
			}

			public Map.Entry next()
			{
				if (next == head)
				{
					throw new NoSuchElementException();
				}
				last= next;
				next= next.next;
				return last;
			}

			public void remove()
			{
				if (last == null)
				{
					throw new IllegalStateException("No current entry");
				}
				last.remove();
				map.remove(last.getKey());
				last= null;
			}
		}

		@Override
		public void clear()
		{
			LinkedHashMap.this.clear();
		}

		@Override
		public boolean contains(Object o)
		{
			if (!(o instanceof Map.Entry))
			{
				return false;
			}
			Map.Entry entry= (Map.Entry) o;
			Object key= entry.getKey();
			if (LinkedHashMap.this.containsKey(key))
			{
				Object value= LinkedHashMap.this.get(key);
				return Utility.equalsWithNullCheck(entry.getValue(), value);
			}
			return false;
		}

		@Override
		public Iterator> iterator()
		{
			return new EntryIterator();
		}

		@Override
		public int size()
		{
			return map.size();
		}
	}

	// True if we should use the access order (ie, for LRU caches) instead of
	// insertion order.
	private transient boolean accessOrder;

	/*
	 * The head of the LRU/insert order chain, which is a doubly-linked circular
	 * list. The key and value of head should never be read.
	 * 
	 * The most recently inserted/accessed node is at the end of the chain, ie.
	 * chain.prev.
	 */
	private final transient ChainEntry head= new ChainEntry();

	/*
	 * The hashmap that keeps track of our entries and the chain. Note that we
	 * duplicate the key here to eliminate changes to HashMap and minimize the
	 * code here, at the expense of additional space.
	 */
	private final transient HashMap map= new HashMap();

	{
		// Initialize the empty linked list.
		head.prev= head;
		head.next= head;
	}

	public LinkedHashMap()
	{
	}

	public LinkedHashMap(int ignored)
	{
		super(ignored);
	}

	public LinkedHashMap(Map toBeCopied)
	{
		this.putAll(toBeCopied);
	}

	@Override
	public void clear()
	{
		map.clear();
		head.prev= head;
		head.next= head;
	}

	@Override
	public boolean containsKey(Object key)
	{
		return map.containsKey(key);
	}

	@Override
	public boolean containsValue(Object value)
	{
		ChainEntry node= head.next;
		while (node != head)
		{
			if (Utility.equalsWithNullCheck(node.getValue(), value))
			{
				return true;
			}
			node= node.next;
		}
		return false;
	}

	@Override
	public Set> entrySet()
	{
		return new EntrySet();
	}

	@Override
	public V get(Object key)
	{
		ChainEntry entry= map.get(key);
		if (entry != null)
		{
			recordAccess(entry);
			return entry.getValue();
		}
		return null;
	}

	@Override
	public V put(K key, V value)
	{
		ChainEntry old= map.get(key);
		if (old == null)
		{
			ChainEntry newEntry= new ChainEntry(key, value);
			map.put(key, newEntry);
			newEntry.addToEnd();
			ChainEntry eldest= head.next;
			if (removeEldestEntry(eldest))
			{
				eldest.remove();
				map.remove(eldest.getKey());
			}
			return null;
		}
		else
		{
			V oldValue= old.getValue();
			old.setValue(value);
			recordAccess(old);
			return oldValue;
		}
	}

	@Override
	public V remove(Object key)
	{
		ChainEntry entry= map.remove(key);
		if (entry != null)
		{
			entry.remove();
			return entry.getValue();
		}
		return null;
	}

	@Override
	public int size()
	{
		return map.size();
	}

	@SuppressWarnings("unused")
	protected boolean removeEldestEntry(Entry eldest)
	{
		return false;
	}

	private void recordAccess(ChainEntry entry)
	{
		if (accessOrder)
		{
			// Move to the tail of the chain on access.
			entry.remove();
			entry.addToEnd();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy