Java tutorial
/* * Copyright 2017 Matt Laquidara. * * 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.bitvantage.bitvantagetypes.collections; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Collection; import java.util.Comparator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.bidimap.TreeBidiMap; /** * Wrapper on the ApacheCommons BidiMap that allows non-comparables and does * not allow re-insertion. * * @author Matt Laquidara */ public class TreeBidirectionalMap<K, V> implements Map<K, V> { private final TreeBidiMap<ComparableWrapper<K>, ComparableWrapper<V>> map; private final Comparator<K> keyComparator; private final Comparator<V> valueComparator; public TreeBidirectionalMap(final Comparator<K> keyComparator, final Comparator<V> valueComparator) { map = new TreeBidiMap(); this.keyComparator = keyComparator; this.valueComparator = valueComparator; } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public boolean containsKey(Object key) { return map.containsKey(new ComparableWrapper<>((K) key, keyComparator)); } @Override public boolean containsValue(Object value) { return map.containsValue(new ComparableWrapper<>((V) value, valueComparator)); } @Override public V get(Object key) { final ComparableWrapper<V> wrappedValue = map.get(new ComparableWrapper<>((K) key, keyComparator)); return (wrappedValue == null) ? null : wrappedValue.getWrapped(); } @Override public V put(K key, V value) { final ComparableWrapper<K> wrappedKey = new ComparableWrapper<>(key, keyComparator); final ComparableWrapper<V> wrappedValue = new ComparableWrapper<>(value, valueComparator); if (map.containsKey(wrappedKey)) { final V preexistingValue = map.get(wrappedKey).getWrapped(); if (!preexistingValue.equals(value)) { throw new IllegalArgumentException( String.format("%s is aready bound to %s.", key, preexistingValue)); } return preexistingValue; } final ComparableWrapper<V> previous = map.put(wrappedKey, wrappedValue); return (previous == null) ? null : previous.getWrapped(); } @Override public V remove(Object key) { return map.remove(new ComparableWrapper<>((K) key, keyComparator)).getWrapped(); } @Override public void putAll(Map<? extends K, ? extends V> m) { for (final Entry<? extends K, ? extends V> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public void clear() { map.clear(); } @Override public Set<K> keySet() { return map.keySet().stream().map((ComparableWrapper<K> k) -> k.getWrapped()).collect(Collectors.toSet()); } @Override public Collection<V> values() { return map.values().stream().map((ComparableWrapper<V> v) -> v.getWrapped()).collect(Collectors.toSet()); } @Override public Set<Entry<K, V>> entrySet() { return map.entrySet().stream() .map((Entry<ComparableWrapper<K>, ComparableWrapper<V>> e) -> new SimpleImmutableEntry<K, V>( e.getKey().getWrapped(), e.getValue().getWrapped())) .collect(Collectors.toSet()); } public K nextKey(final K key) { final ComparableWrapper<K> wrappedKey = map.nextKey(new ComparableWrapper<>(key, keyComparator)); return (wrappedKey == null) ? null : wrappedKey.getWrapped(); } public K previousKey(final K key) { final ComparableWrapper<K> wrappedKey = map.previousKey(new ComparableWrapper<>(key, keyComparator)); return (wrappedKey == null) ? null : wrappedKey.getWrapped(); } public V nextValue(final V value) { final ComparableWrapper<V> wrappedValue = map.inverseBidiMap() .nextKey(new ComparableWrapper<>(value, valueComparator)); return (wrappedValue == null) ? null : wrappedValue.getWrapped(); } public V previousValue(final V value) { final ComparableWrapper<V> wrappedValue = map.inverseBidiMap() .previousKey(new ComparableWrapper<>(value, valueComparator)); return (wrappedValue == null) ? null : wrappedValue.getWrapped(); } public K getKey(final V value) { return map.getKey(new ComparableWrapper<>(value, valueComparator)).getWrapped(); } @RequiredArgsConstructor private static class ComparableWrapper<T> implements Comparable<ComparableWrapper<T>> { @Getter private final T wrapped; private final Comparator<T> comparator; @Override public int compareTo(final ComparableWrapper<T> o) { return comparator.compare(wrapped, o.wrapped); } } }