java.util.LinkedHashMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dragome-js-jre Show documentation
Show all versions of dragome-js-jre Show documentation
Dragome SDK module: js-jre
/*
* 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 extends K, ? extends V> 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();
}
}
}