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 key->value and value->key
* mappings. This implementation utilizes the mechanisms of the
* {@link ObservableMap} to guarantee operational symmetry between the
* {@code this} map and the {@link #inverse()} map. Methods defined in the
* {@code Map} interface that result in a mutation to the {@code this} instance
* will also result in reflected operations to the {@link #inverse()} 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 inverse;
/**
* 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) {
inverse = newEmptyInverseMap();
setTarget(forward);
inverse.setTarget(reverse);
inverse.inverse = this;
}
/**
* Creates an empty instance.
*/
protected BiMap() {
}
/**
* Sets the specified map as the underlying target map of this {@code BiMap}.
*
* @param map The map to set at the underlying target of this {@code 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.inverse.target).target.put(newValue, key);
if (oldValue != null)
((ObservableMap)BiMap.this.inverse.target).target.remove(oldValue);
return true;
}
@Override
protected void afterRemove(final Object key, final V value, final RuntimeException re) {
((ObservableMap)BiMap.this.inverse.target).target.remove(value);
}
};
}
/**
* Returns a new instance of an empty inverse subclass of {@code BiMap}.
*
* @return A new instance of an empty inverse {@code BiMap}.
*/
protected abstract BiMap newEmptyInverseMap();
/**
* Returns the inverse of this map, maintaining value->key mappings.
* Mutations to the {@code inverse()} map are reflected in {@code this} map.
*
* @return The inverse map.
*/
public Map inverse() {
return inverse;
}
@Override
@SuppressWarnings("unlikely-arg-type")
public boolean containsValue(final Object value) {
return inverse.containsKey(value);
}
protected ObservableSet keySet;
@Override
public Set keySet() {
return keySet == null ? keySet = new ObservableSet(target.keySet()) {
@Override
@SuppressWarnings("unchecked")
protected void afterRemove(final Object o, final RuntimeException re) {
BiMap.this.inverse.target.remove(((Map.Entry)o).getValue());
}
} : keySet;
}
protected ObservableCollection values;
@Override
public Collection values() {
return values == null ? values = new ObservableCollection(target.values()) {
@Override
protected void afterRemove(final Object o, final RuntimeException re) {
BiMap.this.inverse.target.remove(o);
}
} : values;
}
protected volatile ObservableSet> entrySet;
@Override
public Set> entrySet() {
return entrySet == null ? entrySet = new ObservableSet>(target.entrySet()) {
@Override
@SuppressWarnings("unchecked")
protected void afterRemove(final Object o, final RuntimeException re) {
BiMap.this.inverse.target.remove(((Map.Entry)o).getValue());
}
} : entrySet;
}
}