views:

58

answers:

3

I have an application without a save button; saving happens automatically in the background. As the user works with the app, tasks are created and put into a queue for execution. Some of them are delayed, for example, when you start typing, I wait 200ms before I update the corresponding value in the database.

To make this more simple to use code wise, I'd like to be able to add a task to the queue with a delay and a "key". If there is already a task with the same "key", then my second task should be ignored (because the same operation is already queued).

Does something like that already exist?

+3  A: 

I think you should look into TreeMap class. Use the constructor which let you pass a Comparator. This way you can let the Map sort on Delayed (since this class implements Comparable interface). Before adding a "Task" to the map, check if the key already exists using containsKey method.

Jeroen Rosenberg
I think this is the best solution. A queue doesn't allow me to get rid of duplicate tasks, so I need a map instead or maybe a TreeMap and a Set (so I can order by delay *and* find duplicate keys).
Aaron Digulla
+1 Somewhat non-obvious, elegant solution.
+1  A: 

This is how I understood the question and would take the challenge:

A task wrapper wraps a task or command and adds the additional values, the delay for the task and the identifier. I understood, that two tasks may have the same 'key', so we can't just take the tasks hashvalue.

public TaskWrapper<T> {
   private T task;
   private long delayInMillis;
   private long key;

   public TaskWrapper(T task, long delayInMillis, long key) {
     this.task = task;
     this.delayInMillis = delayInMillis;
     this.key = key;
   }

   // getter, setters, and so on

   public boolean equals(Object o) {
      if (o == null || !(o instanceof TaskWrapper)) return false;
      return key == ((TaskWrapper) o).key;
   }
}

A queue decorator adds the 'set' like behaviour. A queue doesn't have 'contains' method, so I take an additional Set to record the objects that are actually enqued. All methods that change the queue will be implemented to keep the consistency between the internal queue and the set. This quick draft is not thread safe and shows just the add method.

public SetQueue<TaskWrapper> implements Queue<T> {
  private Queue<TaskWrapper> queue;
  private Set<TaskWrapper> set = new HashSet<TaskWrapper>();

  public SetQueue(Queue<TaskWrapper> queue) {
    this.queue = queue;
  }

  // just to demonstrate the idea
  public boolean add(TaskWrapper<?> task) {
     if (set.contains(task) 
        return false;
     boolean result = queue.add(task);
     if (result) 
        set.add(task);
     return result;
  }

(may be full of bugs, I don't have an IDE at hand. It's just a draft)

Andreas_D
A: 

One of the elegant solution that came into my mind: use a thread pool executer that has a priority blocking queue as working queue.

Loop
What happens when I add an identical task to the queue? If I understand queues correctly, then there can be many tasks that compare equals.
Aaron Digulla
There is no guarantees about the ordering of elements with equal priority. If you need to enforce an ordering, you can define custom classes or comparators that use a secondary key to break ties in primary priority values.
Loop
@Loop: Might a DelayQueue be a better fit?