時間を指定して、その時点での状態を取れるようなMapを作ってみた。
何に使うかとかは考えてない。
こういった、データの世代別管理をして過去のある時点をスナップショットとして取得できるようなデータ構造は、扱うデータが巨大になってバックアップのためにシステムを止めれないというときに重要になったりしますね。
あと、データを変更せず追記する一方なので、ストレージの仕組みとして使うと、変更回数の制限があるSSDと相性がいいとか。
使うとこんな感じ。
public static void main(String[] args) throws InterruptedException{ VersionedMap<String, Integer> vm = new VersionedMap<String, Integer>(); vm.put("abc", 3); vm.put("aaa", 6); vm.put("hoge", 10); System.out.println("--t1--"); print(vm); sleep(100); Date t1 = new Date(); sleep(100); vm.put("abc", 12); vm.put("bbb", 33); vm.remove("hoge"); System.out.println("--t2--"); print(vm); System.out.println("--t1--"); print(vm.getSnapShot(t1)); } static void print(Map<String, Integer> m){ for(Map.Entry<String, Integer> me : m.entrySet()){ System.out.printf("%s - %d%n", me.getKey(), me.getValue()); } }
動かすとこう。
--t1-- abc - 3 aaa - 6 hoge - 10 --t2-- aaa - 6 bbb - 33 abc - 12 --t1-- abc - 3 aaa - 6 hoge - 10
本体は、これ。
実際に使うには、entrySet()とかkeySet()をちゃんと実装しないといけないけど。
あと、スナップショットとるときにちゃんと2分探索にするとか、データの有効期限を決めるとか。
import java.util.*; public class VersionedMap<K, V> implements Map<K, V>{ static class Data<V>{ V data; long time = new Date().getTime(); boolean exist = true; Data(){ exist = false; } Data(V d){ data = d; } } private Map<K, ArrayList<Data<V>>> repos = new HashMap<K, ArrayList<Data<V>>>(); private int currentCount = 0; @Override public int size() { return currentCount; } @Override public boolean isEmpty() { return currentCount == 0; } @Override public boolean containsKey(Object key) { ArrayList<Data<V>> list = repos.get(key); if(list == null) return false; return list.get(list.size() - 1).exist; } @Override public boolean containsValue(Object value) { for(ArrayList<Data<V>> dt : repos.values()){ Data<V> d = dt.get(dt.size() - 1); if(!d.exist) continue; if(d.data == null){ if(value == null) return true; }else{ if(d.data.equals(value)) return true; } } return false; } @Override public V get(Object key) { ArrayList<Data<V>> d = repos.get(key); if(d == null) return null; return d.get(d.size() - 1).data; } @Override public V put(K key, V value) { if(repos.containsKey(key)){ ArrayList<Data<V>> d = repos.get(key); Data<V> dt = d.get(d.size() - 1); d.add(new Data(value)); if(!dt.exist) currentCount++; return dt.data; }else{ ArrayList<Data<V>> d = new ArrayList<Data<V>>(); d.add(new Data<V>(value)); repos.put(key, d); currentCount++; return null; } } @Override public V remove(Object key) { if(repos.containsKey(key)){ ArrayList<Data<V>> d = repos.get(key); Data<V> dt = d.get(d.size() - 1); if(!dt.exist) return null; d.add(new Data<V>()); currentCount--; return dt.data; }else{ return null; } } @Override public void putAll(Map<? extends K, ? extends V> m) { for(Map.Entry<? extends K, ? extends V> me : m.entrySet()){ put(me.getKey(), me.getValue()); } } @Override public void clear() { for(ArrayList<Data<V>> list : repos.values()){ Data<V> dt = list.get(list.size() - 1); if(dt.exist){ list.add(new Data<V>()); } } currentCount = 0; } @Override public Set<K> keySet() { Set<K> ret = new HashSet<K>(); for(Map.Entry<K, ArrayList<Data<V>>> me : repos.entrySet()){ Data<V> last = me.getValue().get(me.getValue().size() - 1); if(last.exist) ret.add(me.getKey()); } return ret; } @Override public Collection<V> values() { Set<V> ret = new HashSet<V>(); for(Map.Entry<K, ArrayList<Data<V>>> me : repos.entrySet()){ Data<V> last = me.getValue().get(me.getValue().size() - 1); if(last.exist) ret.add(last.data); } return ret; } @Override public Set<Map.Entry<K, V>> entrySet() { Set<Map.Entry<K, V>> ret = new HashSet<Map.Entry<K, V>>(); for(Map.Entry<K, ArrayList<Data<V>>> me : repos.entrySet()){ Data<V> last = me.getValue().get(me.getValue().size() - 1); if(last.exist) ret.add(new AbstractMap.SimpleEntry<K, V>(me.getKey(), last.data)); } return ret; } public Map<K, V> getSnapShot(Date d){ Map<K, V> ret = new HashMap<K, V>(); long time = d.getTime(); for(Map.Entry<K, ArrayList<Data<V>>> me : repos.entrySet()){ ArrayList<Data<V>> data = me.getValue(); Data<V> result = null; for(Data<V> dt: data){ if(dt.time > time){ break; } result = dt; } if(result != null && result.exist){ ret.put(me.getKey(), result.data); } } return ret; } }