Java tutorial
/* * Copyright 2010 The Scripps Research Institute * * 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 edu.scripps.fl.collections; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; /** * A SortedMap implementation for dealing with fuzzy keys. A binary search is used to find the closest key to the input. * * @author Mark Southern (southern at scripps dot edu). */ public class FuzzyMap<K, V> extends AbstractMap<K, V> implements SortedMap<K, V> { protected static class ComparableComparator<T extends Comparable> implements Comparator<T> { public int compare(T a, T b) { return a == null ? (b == null ? 0 : -1) : (b == null ? 1 : a.compareTo(b)); } } protected static class Entry<K, V> implements Map.Entry<K, V> { private boolean eq(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } K key; V value; public Entry(K key, V value) { this.key = key; this.value = value; } public Entry(Map.Entry<K, V> e) { this.key = e.getKey(); this.value = e.getValue(); } public boolean equals(Map.Entry<K, V> o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K, V> e = (Map.Entry<K, V>) o; return eq(getKey(), e.getKey()) && eq(getValue(), e.getValue()); } public K getKey() { return key; } public V getValue() { return value; } public int hashCode() { return ((getKey() == null) ? 0 : getKey().hashCode()) ^ ((getValue() == null) ? 0 : getValue().hashCode()); } public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public String toString() { return String.format("%s [%s=%s]", getClass().getName(), getKey(), getValue()); } } @SuppressWarnings("unchecked") static class MapEntryKeyComparator<K> implements Comparator { private Comparator<K> comparator; public MapEntryKeyComparator(Comparator<K> comparator) { this.comparator = comparator; } public int compare(Object o1, Object o2) { Map.Entry e = (Map.Entry) o1; int cmp = comparator.compare((K) e.getKey(), (K) o2); return cmp; } public Comparator<K> getComparator() { return this.comparator; } } private MapEntryKeyComparator<K> comparator = null; protected List<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>(); public FuzzyMap() { comparator = new MapEntryKeyComparator<K>(new ComparableComparator()); } // ---------------------------------------------------------------------- public FuzzyMap(Comparator<K> comparator) { this.comparator = new MapEntryKeyComparator<K>(comparator); } public FuzzyMap(Map<K, V> map) { loadFromMap(this, map); } @Override public void clear() { list.clear(); } @Override public Object clone() { return loadFromMap(new FuzzyMap<K, V>(comparator()), this); } @Override public Comparator<K> comparator() { return comparator.getComparator(); } @Override @SuppressWarnings("unchecked") public boolean containsKey(Object key) { int idx = Collections.binarySearch((List) list, key, comparator); if (idx > 0) return true; else if (idx == 0) return list.size() > 0 && list.get(0).getKey().equals(key) ? true : false; else return false; } // ---------------------------------------------------------------------- @Override public Set<Map.Entry<K, V>> entrySet() { return new AbstractSet<Map.Entry<K, V>>() { public Iterator<Map.Entry<K, V>> iterator() { return list.iterator(); } public int size() { return list.size(); } }; } // ---------------------------------------------------------------------- @Override public K firstKey() { return ((Map.Entry<K, V>) list.get(0)).getKey(); } @Override @SuppressWarnings("unchecked") public V get(Object key) { if (list.isEmpty()) return null; int idx = Collections.binarySearch((List) list, key, comparator); if (idx < 0) { idx = -1 * idx - 2; if (idx < 0) idx = 0; } Map.Entry<K, V> entry = list.get(idx); return entry.getValue(); } public Map.Entry<K, V> getEntry(int index) { return list.get(index); } @SuppressWarnings("unchecked") protected int getIndex(K key) { int idx = Collections.binarySearch((List) list, key, comparator); if (idx < 0) { idx = -1 * idx - 2; if (idx < 0) idx = 0; } return idx; } @Override public SortedMap<K, V> headMap(K toKey) { int idx = getIndex(toKey); FuzzyMap<K, V> fm = new FuzzyMap<K, V>(comparator()); fm.list = list.subList(0, idx); return fm; } @Override public boolean isEmpty() { return list.isEmpty(); } @Override public K lastKey() { return ((Map.Entry<K, V>) list.get(list.size() - 1)).getKey(); } protected FuzzyMap<K, V> loadFromMap(FuzzyMap<K, V> fm, Map<K, V> map) { for (Map.Entry<K, V> entry : map.entrySet()) { fm.put(entry.getKey(), entry.getValue()); } return fm; } @Override @SuppressWarnings("unchecked") public V put(K key, V value) { Entry<K, V> entry = new Entry<K, V>(key, value); int idx = Collections.binarySearch((List) list, key, comparator); if (idx >= 0) { Map.Entry<K, V> oldEntry = list.set(idx, entry); return oldEntry.getValue(); } else { idx = -1 * idx - 1; list.add(idx, entry); return null; } } @Override @SuppressWarnings("unchecked") public V remove(Object key) { if (list.isEmpty()) return null; int idx = Collections.binarySearch((List) list, key, comparator); if (idx >= 0) { Map.Entry<K, V> entry = list.remove(idx); return entry.getValue(); } return null; } @Override public int size() { return list.size(); } @Override public SortedMap<K, V> subMap(K fromKey, K toKey) { int idx1 = getIndex(fromKey); int idx2 = getIndex(toKey); FuzzyMap<K, V> fm = new FuzzyMap<K, V>(comparator()); fm.list = list.subList(idx1, idx2); return fm; }; @Override public SortedMap<K, V> tailMap(K fromKey) { int idx = getIndex(fromKey); FuzzyMap<K, V> fm = new FuzzyMap<K, V>(comparator()); fm.list = list.subList(idx, list.size() - 1); return fm; } }