blob: 35402a6004459d5cd5bf7018b9fce14795d96fd3 [file] [log] [blame]
/*
* Copyright 2011 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 com.google.gwt.user.server.rpc.impl;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
/**
* MultiMap with stack semantics for the values.
*
* Initial insertion of a given key results in a new stack containing the value.
* Subsequent puts for the same key add the value to the stack. Removing a key
* removes its value from the stack.
*
* @param <K> The key to use in the map
* @param <V> The value to put in the deque
*/
public class DequeMap<K, V> {
/**
* The default initial capacity for the Deque objects.
*/
private static final int DEFAULT_INITIAL_DEQUE_CAPACITY = 3;
/**
* The default initial capacity for the Deque objects.
*/
private static final int DEFAULT_INITIAL_MAP_CAPACITY = 3;
/**
* The initial capacity for the Deque objects.
*/
private int dequeCapacity = DEFAULT_INITIAL_DEQUE_CAPACITY;
/**
* The map used to hold data.
*
* Note that we do not extend map because we require control over the
* addition and removal of elements from the map.
*/
private final HashMap<K, ArrayDeque<V>> map;
private int size = 0;
/**
* Constructs an empty <tt>DequeMap</tt> with the default initial capacities
* (3 and 3) and the default map load factor (0.75).
*/
public DequeMap() {
this(DEFAULT_INITIAL_MAP_CAPACITY, DEFAULT_INITIAL_DEQUE_CAPACITY);
}
/**
* Constructs an empty <tt>DequeMap</tt> with the specified initial capacities
* and the default map load factor (0.75).
*
* @param initialMapCapacity the initial map capacity.
* @param initialDequeCapacity the initial deque capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/
public DequeMap(int initialMapCapacity, int initialDequeCapacity) {
map = new HashMap<K, ArrayDeque<V>>(initialMapCapacity);
dequeCapacity = initialDequeCapacity;
}
/**
* Add a value for a key to the map.
*
* If the key already exists, the value is added to the head of the queue at
* that key. Otherwise, a new queue containing the value is created and
* inserted into the map at the key.
*
* @param key key with which the specified value is to be associated
* @param value the value to push for the key
*/
public void add(K key, V value) {
ArrayDeque<V> deque = map.get(key);
if (deque == null) {
deque = new ArrayDeque<V>(dequeCapacity);
deque.addFirst(value);
map.put(key, deque);
size++;
} else {
if (deque.isEmpty()) {
size++;
}
deque.addFirst(value);
}
}
/**
* Get the most recent value for a key.
*
* @param key key to get
* @return the value at the head of the stack for the key, or null if the key
* is unknown.
*/
public V get(K key) {
Deque<V> deque = map.get(key);
if (deque == null) {
return null;
}
return deque.peekFirst();
}
/**
* Remove the most recent value for a key.
*
* @param key key to get
* @return the value at the head of the stack for the key, or null if the key
* is unknown.
*/
public V remove(K key) {
Deque<V> deque = map.get(key);
if (deque == null) {
return null;
}
boolean wasEmpty = deque.isEmpty();
V result = deque.pollFirst();
if (deque.isEmpty() && !wasEmpty) {
assert size > 0;
size--;
}
return result;
}
/**
* Returns true if no mappings are defined.
*/
public boolean isEmpty() {
return size == 0;
}
}