org.libj.util.BiMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of util Show documentation
Show all versions of util Show documentation
Supplementary utilities for classes that belong to java.util, or are considered essential as to justify existence in java.util.
/* Copyright (c) 2017 LibJ
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* You should have received a copy of The MIT License (MIT) along with this
* program. If not, see .
*/
package org.libj.util;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* Bidirectional map that maintains both {@code key->value} and
* {@code value->key} mappings. This implementation utilizes the mechanisms of
* the {@link ObservableMap} to guarantee operational symmetry between the
* {@code this} map and the {@link #reverse()} map. Methods defined in the
* {@link Map} interface that result in a mutation to the {@code this} instance
* will also result in reflected operations to the {@link #reverse()} instance.
* This implementation is not synchronized.
*
* @param The type of keys maintained by this map.
* @param The type of mapped values.
* @see ObservableMap
* @see DelegateMap
*/
public abstract class BiMap extends DelegateMap {
protected volatile BiMap reverse;
/**
* Construct a new bidirectional map with the provided source maps.
*
* @param forward The forward source map.
* @param reverse The reverse source map.
*/
protected BiMap(final Map forward, final Map reverse) {
setTarget(forward);
this.reverse = newEmptyReverseMap();
this.reverse.setTarget(reverse);
this.reverse.reverse = this;
}
/**
* Creates an empty instance.
*/
protected BiMap() {
}
/**
* Sets the specified map as the underlying target map of this {@link BiMap}.
*
* @param map The map to set at the underlying target of this {@link BiMap}.
*/
protected void setTarget(final Map map) {
super.target = new ObservableMap(map) {
@Override
protected boolean beforePut(final K key, final V oldValue, final V newValue) {
((ObservableMap)BiMap.this.reverse.target).target.put(newValue, key);
if (oldValue != null)
((ObservableMap)BiMap.this.reverse.target).target.remove(oldValue);
return true;
}
@Override
protected void afterRemove(final Object key, final V value, final RuntimeException e) {
((ObservableMap)BiMap.this.reverse.target).target.remove(value);
}
};
}
/**
* Returns a new instance of an empty reverse subclass of {@link BiMap}.
*
* @return A new instance of an empty reverse subclass of {@link BiMap}.
*/
protected abstract BiMap newEmptyReverseMap();
/**
* Returns the reverse of this map, maintaining {@code value->key} mappings.
* Mutations to the map returned by this method map are reflected in this map.
*
* @return The reverse map.
*/
public Map reverse() {
return reverse;
}
@Override
@SuppressWarnings("unlikely-arg-type")
public boolean containsValue(final Object value) {
return reverse.containsKey(value);
}
protected volatile ObservableSet keySet;
@Override
public Set keySet() {
return keySet == null ? keySet = new ObservableSet(target.keySet()) {
private final ThreadLocal localValue = new ThreadLocal<>();
@Override
protected boolean beforeAdd(final K element) {
throw new UnsupportedOperationException();
}
@Override
@SuppressWarnings("unlikely-arg-type")
protected boolean beforeRemove(final Object element) {
if (!BiMap.this.containsKey(element))
return false;
final V value = BiMap.this.get(element);
localValue.set(value);
return true;
}
@Override
protected void afterRemove(final Object element, final RuntimeException e) {
BiMap.this.reverse.target.remove(localValue.get());
}
} : keySet;
}
protected volatile ObservableCollection values;
@Override
public Collection values() {
return values == null ? values = new ObservableCollection(target.values()) {
@Override
protected boolean beforeAdd(final V element) {
throw new UnsupportedOperationException();
}
@Override
protected void afterRemove(final Object element, final RuntimeException e) {
BiMap.this.reverse.target.remove(element);
}
} : values;
}
protected volatile ObservableSet> entrySet;
@Override
public Set> entrySet() {
return entrySet == null ? entrySet = new ObservableSet>(target.entrySet()) {
@Override
protected boolean beforeAdd(final Entry element) {
throw new UnsupportedOperationException();
}
@Override
@SuppressWarnings("unchecked")
protected void afterRemove(final Object element, final RuntimeException e) {
BiMap.this.reverse.target.remove(((Map.Entry)element).getValue());
}
} : entrySet;
}
}