Dépôt officiel du code source de l'ERP OpenConcerto
Rev 83 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.utils;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
public class TinyMap<K, V> extends AbstractMap<K, V> {
private static final Function<Entry<?, ?>, Object> KEY_GETTER = Entry::getKey;
private static final Function<Entry<?, ?>, Object> VALUE_GETTER = Entry::getValue;
private final ArrayList<Entry<K, V>> entries;
private transient Set<Map.Entry<K, V>> entrySet;
/**
* The number of times this HashMap has been structurally modified Structural modifications are
* those that change the number of mappings in the HashMap or otherwise modify its internal
* structure (e.g., rehash). This field is used to make iterators on Collection-views of the
* HashMap fail-fast. (See ConcurrentModificationException).
*/
transient int modCount = 0;
public TinyMap() {
this(10);
}
public TinyMap(final int initialCapacity) {
this.entries = new ArrayList<>(initialCapacity);
}
public TinyMap(final Map<? extends K, ? extends V> m) {
this.entries = new ArrayList<>(m.size());
// don't call putAll() to avoid indexOfKey()
for (final Entry<? extends K, ? extends V> e : m.entrySet()) {
this.entries.add(new SimpleEntry<>(e));
}
++this.modCount;
}
@Override
public int size() {
return this.entries.size();
}
@Override
public boolean isEmpty() {
return this.entries.isEmpty();
}
@Override
public boolean containsKey(final Object key) {
return indexOfKey(key) >= 0;
}
@Override
public boolean containsValue(final Object value) {
return this.indexOf(value, VALUE_GETTER) >= 0;
}
private int indexOfKey(final Object key) {
return this.indexOf(key, KEY_GETTER);
}
private final int indexOf(final Object o, final Function<Entry<?, ?>, Object> getter) {
final int stop = this.entries.size();
for (int i = 0; i < stop; i++) {
final Entry<K, V> e = this.entries.get(i);
if (Objects.equals(o, getter.apply(e)))
return i;
}
return -1;
}
@Override
public V get(final Object key) {
final int i = indexOfKey(key);
if (i < 0)
return null;
return this.entries.get(i).getValue();
}
@Override
public V put(final K key, final V value) {
final int i = this.indexOfKey(key);
final V res;
if (i < 0) {
this.entries.add(new SimpleEntry<>(key, value));
++this.modCount;
res = null;
} else {
res = this.entries.get(i).setValue(value);
}
return res;
}
@Override
public V remove(final Object key) {
return this.remove(indexOfKey(key));
}
private V remove(final int i) {
if (i < 0)
return null;
final Entry<K, V> res = this.entries.remove(i);
++this.modCount;
return res.getValue();
}
@Override
public boolean remove(final Object key, final Object value) {
final int i = indexOfKey(key);
if (i < 0)
return false;
final boolean eqVal = Objects.equals(this.entries.get(i).getValue(), value);
if (eqVal) {
this.remove(i);
}
return eqVal;
}
@Override
public void clear() {
this.entries.clear();
++this.modCount;
}
// Views
@Override
public Set<Map.Entry<K, V>> entrySet() {
Set<Map.Entry<K, V>> es;
return (es = this.entrySet) == null ? (this.entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
@Override
public final int size() {
return TinyMap.this.size();
}
@Override
public final void clear() {
TinyMap.this.clear();
}
@Override
public final Iterator<Map.Entry<K, V>> iterator() {
return new Iterator<Map.Entry<K, V>>() {
private int expectedModCount = TinyMap.this.modCount;
/**
* The index last returned, i.e. initial value just before first item.
*
* <pre>
* a b c
* -1 0 1 2
* </pre>
*/
private int currentPos = -1;
private Entry<K, V> lastReturned = null;
@Override
public boolean hasNext() {
final int nextIndex = this.currentPos + 1;
return nextIndex < size();
}
@Override
public Entry<K, V> next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
this.currentPos++;
final Entry<K, V> res = TinyMap.this.entries.get(this.currentPos);
this.lastReturned = res;
return res;
}
@Override
public void remove() {
checkForComodification();
if (this.lastReturned == null)
throw new IllegalStateException();
TinyMap.this.remove(this.currentPos);
this.currentPos--;
// per doc : cannot be called twice
this.lastReturned = null;
this.expectedModCount = TinyMap.this.modCount;
}
final void checkForComodification() {
if (TinyMap.this.modCount != this.expectedModCount)
throw new ConcurrentModificationException();
}
};
}
@Override
public final boolean contains(final Object o) {
if (!(o instanceof Map.Entry))
return false;
final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
return TinyMap.this.entries.contains(e);
}
@Override
public final boolean remove(final Object o) {
if (o instanceof Map.Entry) {
final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
final Object key = e.getKey();
final Object value = e.getValue();
return TinyMap.this.remove(key, value);
}
return false;
}
}
}