3

I want to implement a very simple sliding window. In other words, I will have some kind of list with objects inserted from the right end of that list and dropped from the left end. In every insertion, the previous objects are left-shifted by one index. When the list get filled with objects, in every insertion from the right end an object will be dropped from the left end (and the previous objects of course will be left-shifted by one index, as usual).

I had in mind either a LinkedList or an ArrayDeque - probably the latter is a better choise, since as far as I know both inserting AND removing to/from either end is constant effort O(1) for an ArrayDeque, which is not the case for a LinkedList. Is that right?

Moreover, I would like to ask the following: Left-shifting all the previous objects stored in the sliding window when I insert a new object is processing-intensive for a large sliding window with 100,000 or even 1,000,000 objects as in my case. Is there any other data structure which might perform better in my application?

NOTE: I use the term "sliding window" for what I want to implement, maybe there is some other term that describes it better, but I think is clear what I want to do from the above description.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
PeterHiggs
  • 97
  • 2
  • 10
  • Do you also need the ability to quickly retrieve an object from the container given an arbitrary index? BTW: `LinkedList` in Java is a double linked list, meaning that inserting/removing at either end is also O(1). – Jason C Feb 26 '14 at 01:54
  • @JasonC No, for this I only need to insert at one end, shift by one index the previous objects, and drop by the other end. I need quick retrieval for another structure, but this problem I solved it (I think, I am testing that now) with a variation of the "counters" solution that you suggested to me in a previous post. – PeterHiggs Feb 26 '14 at 01:58
  • @JasonC About LinkedList, I ran a code few days ago with a LinkedList with size varying from 100,000 to 1,000,000 used as a sliding window. It seemed to me that it took a very long time, and I assumed that this might be due to the shifting of the objects by one index each time. Apparently I was wrong! – PeterHiggs Feb 26 '14 at 02:03
  • There is also the [`CircularFifoQueue`](http://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/queue/CircularFifoQueue.html) from [Apache Commons Collections](http://commons.apache.org/proper/commons-collections/); the implementation is similar to an `ArrayQueue` but it will automatically remove old elements as new ones are added. – Jason C Feb 26 '14 at 02:30

2 Answers2

5

ArrayDeque does what you want. It doesn't move the elements around. It moves the index of where the start and the end is. When you add an element, the end counter moves and when you remove an element, the start counter moves.

One advantage of ArrayDeque is that it can use less memory and does create garbage. On the down side it has a fixed maximum size. LinkedList grows and shrinks.

BTW If you want a light weight sliding window or the average of some values, an exponentially weighted moving average is much cheaper as you only need to record two values, the previous and last time.

e.g

double last = 0;
long lastTime = 0;
double halfLife = 60 * 1000; // 60 seconds for example.

public static double ewma(double sample, long time) {
    double alpha = Math.exp((lastTime - time) / halfLife);
    lastTime = time;
    return last = sample * alpha + last * (1 - alpha); 
}

or you can approximate this to avoid calling Math.exp with

public static double ewma(double sample, long time) {
    long delay = time - lastTime
    double alpha = delay >= halfLife ? 1.0 : delta / halfLife;
    lastTime = time;
    return last = sample * alpha + last * (1 - alpha); 
}

This is many times faster and for short intervals gives much the same result.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Therefore, if I understand right, I have the effect that I want - a sliding window of objects - without the performance penalnty of shifting objects by one index, since actually they are the indexes that change accordingly instead of the objects being moved (which is equivalent), right? If that's the case ... :)! – PeterHiggs Feb 25 '14 at 22:37
  • @PeterHiggs Correct except the ArrayQueue only contains references to the Objects, not the objects themselves. E.g. if you remove from the start of an ArrayList, all the references have to shift down, but the objects don't move. – Peter Lawrey Feb 25 '14 at 22:44
  • ok, I will give it a try and inform you about the result. Thanks! – PeterHiggs Feb 25 '14 at 22:46
0

Are you talking about a Queue? Take a look at java.util.LinkedList implementation, as it implements the Queue interface. Also LinkedList's both push and pop complexity is O(1), but get's is O(N).

Edit: This is the core of LinkedList's add method:

Link<ET> next = link.next;
Link<ET> newLink = new Link<ET>(object, link, next);
link.next = newLink;
next.previous = newLink;
link = newLink;
lastLink = null;
pos++;
expectedModCount++;
list.size++;
list.modCount++;
Jason C
  • 38,729
  • 14
  • 126
  • 182
abraabra
  • 464
  • 3
  • 10
  • Yes, I think the term Queue or even better FIFO describes what I mean. A doubly-linked list (which is what the LinkedList implementation of the List interface is actually in Java) should work fine. However, I still wonder, since every time that I insert an object from one end of the LinkedList and drop another object from the other end all the objects in the LinkedList have to be shifted by one index, isn't that processing-demanding and for sure not constant O(1) effort? I mean, the larger the LinkedList, the more time will take, right? – PeterHiggs Feb 25 '14 at 22:26
  • I don't want to get, just push and pop (add and remove methods, to be precise). I was just wondering how push is O(1) since all the other objects have to be shifted by one index. – PeterHiggs Feb 25 '14 at 22:30
  • Nope, LinkedList's add's complexity is O(1), meaning it is constant, i.e. doesn't change. Internally LinkedList doesn't hold stuff in an array, but in a bunch of nodes connected to each other, so there are no indices. To add an object you have to create a node and connect it to the "old" head node, nevermind the nodes the "old" one may lead to. – abraabra Feb 25 '14 at 22:31
  • Thanks for your reply. I will trie both a LinkedList and an ArrayDeque in my code and I will check the result and then give feedback. Always the code is at the end the best way to find out, but at least now I know I am at the right track! – PeterHiggs Feb 25 '14 at 22:48