6
\$\begingroup\$

Here's a class I made in java. It's pretty simple, the description of what it does is in the comment. I'd appreciate any feedback.

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * An unordered, duplicate-allowing collection of items that are added with a
 * time-to-live, and will automatically be considered not a part of the
 * collection when that time expires. Has 0 thread overhead, and is not thread safe.
 */
public class TTLBag<E> implements Iterable<E> {

    private class Element {

        E item;
        long expires;

        Element(E item, long expires) {
            this.item = item;
            this.expires = expires;
        }

    }

    private List<Element> contents = new ArrayList<>();

    public void add(E item, long timeToLive) {
        contents.add(new Element(item, System.currentTimeMillis() + timeToLive));
    }

    @Override
    public Iterator<E> iterator() {
        long time = System.currentTimeMillis();
        contents.removeIf(element -> element.expires > time);
        return contents.stream().map(element -> element.item).iterator();
    }

}
\$\endgroup\$
  • 4
    \$\begingroup\$ Adding few unit tests would be a good start \$\endgroup\$ – JBM Mar 24 '17 at 22:47
  • 2
    \$\begingroup\$ Be aware that currentTimeMillis() is not monotonic - it jumps back and forth whenever the time is synced to a time server. A better alternative is System.nanoTime() stackoverflow.com/questions/351565/… \$\endgroup\$ – JBM Mar 24 '17 at 23:12
  • 2
    \$\begingroup\$ Not sure if it makes a problem, but the expiration logic is applied at the moment the iterator is requested. However, the actual access to an item will happen later; this way, items which expire by that moment are still presented to the client. \$\endgroup\$ – JBM Mar 24 '17 at 23:20
  • 2
    \$\begingroup\$ I support the comment of @JBM. But even if you implement the iteraror interface yourself: There will always be a time delay of unknown length between the call to hasNext() and next() where an element could reach its expiration, so that especially at the end of the iteration hasNext() may return true whereas next() has no element left to deliver and needs to throw a NoSuchElementException. You may lower the risk by sorting the elements by expiration time ascending, since already delivered elements may not affect the not yet delivered elements when expiring. \$\endgroup\$ – Timothy Truckle Mar 25 '17 at 12:04
1
\$\begingroup\$

You remove the items when the iterator is requested. Are you sure that this is right in your use case? Imagine the user puts an item in with a lifetime of 20 ms, requests the iterator, gets it with this one element and uses the iterator later. Theoretically, the item would be dead then. You should mention this behavior of the iterator method.

Additionally, the performance of the iterator construction depends on the number of dead entities inside the collection -> mention it.

\$\endgroup\$
0
\$\begingroup\$

Ok, I would recommend going with TDD - test-driven development. If you write this not for fun, but for production, then usually it needs to be reliable. I would say it makes sense to describe the idea and your particular case. Check PassiveExpiringMap for example. Usually people don't reinvent the wheel - there are libraries that are well designed and its all about knowing what to use and when. Its also good to learn from reliable libraries. Ehcache also performs expiring cache.

Overall if I missed the idea - makes sense to describe what you're trying to achieve, so it will be easier to help you.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.