Skip to content

Commit 3956ca2

Browse files
Merge pull request ReactiveX#413 from zsxwing/take-last
Fixed the issues of takeLast(items, 0) and null values
2 parents eaff4d5 + 5200cfc commit 3956ca2

File tree

1 file changed

+88
-12
lines changed

1 file changed

+88
-12
lines changed

rxjava-core/src/main/java/rx/operators/OperationTakeLast.java

+88-12
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,16 @@
1515
*/
1616
package rx.operators;
1717

18-
import static org.mockito.Matchers.*;
19-
import static org.mockito.Mockito.*;
18+
import static org.mockito.Matchers.any;
19+
import static org.mockito.Mockito.inOrder;
20+
import static org.mockito.Mockito.mock;
21+
import static org.mockito.Mockito.never;
22+
import static org.mockito.Mockito.times;
23+
import static org.mockito.Mockito.verify;
2024

21-
import java.util.Iterator;
22-
import java.util.concurrent.LinkedBlockingDeque;
25+
import java.util.Deque;
26+
import java.util.LinkedList;
27+
import java.util.concurrent.locks.ReentrantLock;
2328

2429
import org.junit.Test;
2530
import org.mockito.InOrder;
@@ -59,25 +64,36 @@ private static class TakeLast<T> implements OnSubscribeFunc<T> {
5964
}
6065

6166
public Subscription onSubscribe(Observer<? super T> observer) {
67+
if (count < 0) {
68+
throw new IndexOutOfBoundsException(
69+
"count could not be negative");
70+
}
6271
return subscription.wrap(items.subscribe(new ItemObserver(observer)));
6372
}
6473

6574
private class ItemObserver implements Observer<T> {
6675

67-
private LinkedBlockingDeque<T> deque = new LinkedBlockingDeque<T>(count);
76+
/**
77+
* Store the last count elements until now.
78+
*/
79+
private Deque<T> deque = new LinkedList<T>();
6880
private final Observer<? super T> observer;
81+
private final ReentrantLock lock = new ReentrantLock();
6982

7083
public ItemObserver(Observer<? super T> observer) {
7184
this.observer = observer;
7285
}
7386

7487
@Override
7588
public void onCompleted() {
76-
Iterator<T> reverse = deque.descendingIterator();
77-
while (reverse.hasNext()) {
78-
observer.onNext(reverse.next());
89+
try {
90+
for (T value : deque) {
91+
observer.onNext(value);
92+
}
93+
observer.onCompleted();
94+
} catch (Throwable e) {
95+
observer.onError(e);
7996
}
80-
observer.onCompleted();
8197
}
8298

8399
@Override
@@ -86,9 +102,27 @@ public void onError(Throwable e) {
86102
}
87103

88104
@Override
89-
public void onNext(T args) {
90-
while (!deque.offerFirst(args)) {
91-
deque.removeLast();
105+
public void onNext(T value) {
106+
if (count == 0) {
107+
// If count == 0, we do not need to put value into deque and
108+
// remove it at once. We can ignore the value directly.
109+
return;
110+
}
111+
lock.lock();
112+
try {
113+
deque.offerLast(value);
114+
if (deque.size() > count) {
115+
// Now deque has count + 1 elements, so the first
116+
// element in the deque definitely does not belong
117+
// to the last count elements of the source
118+
// sequence. We can drop it now.
119+
deque.removeFirst();
120+
}
121+
} catch (Throwable e) {
122+
observer.onError(e);
123+
subscription.unsubscribe();
124+
} finally {
125+
lock.unlock();
92126
}
93127
}
94128

@@ -140,6 +174,48 @@ public void testTakeLast2() {
140174
verify(aObserver, times(1)).onCompleted();
141175
}
142176

177+
@Test
178+
public void testTakeLastWithZeroCount() {
179+
Observable<String> w = Observable.from("one");
180+
Observable<String> take = Observable.create(takeLast(w, 0));
181+
182+
@SuppressWarnings("unchecked")
183+
Observer<String> aObserver = mock(Observer.class);
184+
take.subscribe(aObserver);
185+
verify(aObserver, never()).onNext("one");
186+
verify(aObserver, never()).onError(any(Throwable.class));
187+
verify(aObserver, times(1)).onCompleted();
188+
}
189+
190+
@Test
191+
public void testTakeLastWithNull() {
192+
Observable<String> w = Observable.from("one", null, "three");
193+
Observable<String> take = Observable.create(takeLast(w, 2));
194+
195+
@SuppressWarnings("unchecked")
196+
Observer<String> aObserver = mock(Observer.class);
197+
take.subscribe(aObserver);
198+
verify(aObserver, never()).onNext("one");
199+
verify(aObserver, times(1)).onNext(null);
200+
verify(aObserver, times(1)).onNext("three");
201+
verify(aObserver, never()).onError(any(Throwable.class));
202+
verify(aObserver, times(1)).onCompleted();
203+
}
204+
205+
@Test
206+
public void testTakeLastWithNegativeCount() {
207+
Observable<String> w = Observable.from("one");
208+
Observable<String> take = Observable.create(takeLast(w, -1));
209+
210+
@SuppressWarnings("unchecked")
211+
Observer<String> aObserver = mock(Observer.class);
212+
take.subscribe(aObserver);
213+
verify(aObserver, never()).onNext("one");
214+
verify(aObserver, times(1)).onError(
215+
any(IndexOutOfBoundsException.class));
216+
verify(aObserver, never()).onCompleted();
217+
}
218+
143219
}
144220

145221
}

0 commit comments

Comments
 (0)