The core of what you are looking for is to keep a list of all hits within the last minute. Using a LinkedList
allows for a fast removeFirst()
.
class HitCounter {
// My most recent list - only the last minute is retained.
LinkedList<Long> hitList = new LinkedList<>();
private static final long OneMinute = 1000L * 60L;
public void hit() {
// Track the hit.
hitList.add(System.currentTimeMillis());
// Always clean up.
cleanup();
}
public int hitCount() {
// Make sure we are clean.
cleanup();
return hitList.size();
}
private void cleanup() {
// Eat all stale hits.
while (hitList.getFirst() < System.currentTimeMillis() - OneMinute) {
hitList.removeFirst();
}
}
}
If, however, your hits come very fast and you doubt it would be efficient to retain all times of all hits you can use a bucket system to quantize. Here I keep counts for each second. Obviously you could be out by as many hits there are in a second.
class HitCounter {
// One bucket - keeps track of how many hits since the start time.
private class Bucket {
// Probably should use an atomic.
int count;
// The time this bucket was started.
long started = System.currentTimeMillis();
}
// My most recent list - only the last minute is retained.
LinkedList<Bucket> hitList = new LinkedList<>();
private static final long OneSecond = 1000L;
private static final long OneMinute = OneSecond * 60L;
public void hit() {
// Grab the hit time.
long now = System.currentTimeMillis();
// Normally goes in the last bucket.
Bucket bucket = hitList.size() > 0 ? hitList.getLast() : null;
// Time for new bucket?
if (bucket == null || now > bucket.started + OneSecond) {
// Yup!
hitList.add(bucket = new Bucket());
}
// Track the hit.
bucket.count++;
// Always clean up.
cleanup();
}
public int hitCount() {
// Make sure we are clean.
cleanup();
// Add up all the bucket counts.
int total = 0;
for (Bucket bucket : hitList) {
total += bucket.count;
}
return total;
}
private void cleanup() {
// Eat all stale hits.
while (hitList.size() > 0 && hitList.getFirst().started < System.currentTimeMillis() - OneMinute - OneSecond) {
hitList.removeFirst();
}
}
}
public void test() throws InterruptedException {
HitCounter hitList = new HitCounter();
for (int i = 0; i < 90; i++) {
hitList.hit();
System.out.println(i + " - " + hitList.hitCount());
Thread.sleep(1000);
}
}