UDP受信のように、同じデータが短い時間間隔で送られてくる可能性がある
場合の受け側で、1回だけを検出して処理する方法を考える。
当初、タイマー付きMapみたいなことを考えたが、マップに格納した要素
1つ1つにタイマースレッドを作成するのは危険だ。
そこで、Key と時刻のマップを内部で持ち、SortedMap#headMap を利用して
remove の実行、チェックしての任意の処理実行を考えた。
public interface CometMapper<K,V>{
public void checkIn(K key,V value);
}
----------------------------------------------------
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap;
import java.util.Map.Entry;
import com.google.inject.Inject;
/**
* CometHashMap.
* 任意の時間間隔で要素が削除される HashMap
* put実行時 containsKey の結果が false の時に任意の処理を実行する。
* コンストラクタで任意の時間間隔 msec と
* 任意の処理メソッドインスタンスを指定する。
*/
public class CometHashMap<K,V> extends HashMap<K,V>{
private static final long serialVersionUID = 1L;
private Timer timer;
private CometMapper mapper;
long checkTime;
Map<String,Long> hmap;
/**
* @param checkTime 削除する時間間隔 msec
* @param mapper 任意の処理
*/
@Inject
public CometHashMap(@CometLiveScorp long checkTime,CometMapper mapper){
super();
this.hmap = new HashMap<String,Long>();
this.mapper = mapper;
this.timer = new Timer("CometHashMap");
this.checkTime = checkTime;
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND,1);
cal.clear(Calendar.MILLISECOND);
this.timer.scheduleAtFixedRate(new _Reseter(),cal.getTime(),checkTime);
}
/*
* @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
*/
@Override
public V put(K key,V value){
return this._put(key,value);
}
@SuppressWarnings("unchecked")
private synchronized V _put(K key,V value){
this.hmap.put(key.toString(),System.currentTimeMillis());
boolean b = super.containsKey(key) ? false : true;
V v = super.put(key,value);
if (b) this.mapper.checkIn(key,value);
return v;
}
class _Reseter extends TimerTask{
private SortedMap<Long,String> smap;
_Reseter(){
this.smap = new TreeMap<Long,String>(
new Comparator<Long>(){
@Override
public int compare(Long l1,Long l2){
return l1.compareTo(l2) >= 0 ? 1 : -1;
}
}
);
}
/*
* @see java.util.TimerTask#run()
*/
@Override
public void run(){
if (CometHashMap.this.hmap.size()==0) return;
this.smap.clear();
for(Entry<String,Long> e : CometHashMap.this.hmap.entrySet()){
this.smap.put(e.getValue(),e.getKey());
}
for(String key : this.smap.headMap(System.currentTimeMillis() - CometHashMap.this.checkTime).values()){
CometHashMap.this.hmap.remove(key);
CometHashMap.this.remove(key);
}
}
}
/**
* GC実行より確実に内部で処理されるマップを削除するタイマーを
* キャンセルさせるために用意した終了処理
*/
public void close(){
try{
this.finalize();
}catch(Throwable e){
}finally{
this.timer.cancel();
}
}
/*
* @see java.lang.Object#finalize()
*/
@Override
protected void finalize() throws Throwable{
this.timer.cancel();
super.finalize();
}
}