16 |
|
import java.util.concurrent.locks.Condition; |
17 |
|
import java.util.concurrent.locks.ReentrantLock; |
18 |
|
import java.util.function.Consumer; |
19 |
+ |
import java.util.function.Predicate; |
20 |
|
|
21 |
|
/** |
22 |
|
* An optionally-bounded {@linkplain BlockingDeque blocking deque} based on |
167 |
|
*/ |
168 |
|
public LinkedBlockingDeque(Collection<? extends E> c) { |
169 |
|
this(Integer.MAX_VALUE); |
170 |
< |
final ReentrantLock lock = this.lock; |
170 |
< |
lock.lock(); // Never contended, but necessary for visibility |
171 |
< |
try { |
172 |
< |
for (E e : c) { |
173 |
< |
if (e == null) |
174 |
< |
throw new NullPointerException(); |
175 |
< |
if (!linkLast(new Node<E>(e))) |
176 |
< |
throw new IllegalStateException("Deque full"); |
177 |
< |
} |
178 |
< |
} finally { |
179 |
< |
// checkInvariants(); |
180 |
< |
lock.unlock(); |
181 |
< |
} |
170 |
> |
addAll(c); |
171 |
|
} |
172 |
|
|
173 |
|
|
815 |
|
} |
816 |
|
} |
817 |
|
|
818 |
< |
/* |
819 |
< |
* TODO: Add support for more efficient bulk operations. |
818 |
> |
/** |
819 |
> |
* Appends all of the elements in the specified collection to the end of |
820 |
> |
* this deque, in the order that they are returned by the specified |
821 |
> |
* collection's iterator. Attempts to {@code addAll} of a deque to |
822 |
> |
* itself result in {@code IllegalArgumentException}. |
823 |
|
* |
824 |
< |
* We don't want to acquire the lock for every iteration, but we |
825 |
< |
* also want other threads a chance to interact with the |
826 |
< |
* collection, especially when count is close to capacity. |
827 |
< |
*/ |
828 |
< |
|
829 |
< |
// /** |
830 |
< |
// * Adds all of the elements in the specified collection to this |
831 |
< |
// * queue. Attempts to addAll of a queue to itself result in |
832 |
< |
// * {@code IllegalArgumentException}. Further, the behavior of |
833 |
< |
// * this operation is undefined if the specified collection is |
834 |
< |
// * modified while the operation is in progress. |
835 |
< |
// * |
836 |
< |
// * @param c collection containing elements to be added to this queue |
837 |
< |
// * @return {@code true} if this queue changed as a result of the call |
838 |
< |
// * @throws ClassCastException {@inheritDoc} |
839 |
< |
// * @throws NullPointerException {@inheritDoc} |
840 |
< |
// * @throws IllegalArgumentException {@inheritDoc} |
841 |
< |
// * @throws IllegalStateException if this deque is full |
842 |
< |
// * @see #add(Object) |
843 |
< |
// */ |
844 |
< |
// public boolean addAll(Collection<? extends E> c) { |
845 |
< |
// if (c == null) |
846 |
< |
// throw new NullPointerException(); |
847 |
< |
// if (c == this) |
848 |
< |
// throw new IllegalArgumentException(); |
849 |
< |
// final ReentrantLock lock = this.lock; |
850 |
< |
// lock.lock(); |
851 |
< |
// try { |
852 |
< |
// boolean modified = false; |
853 |
< |
// for (E e : c) |
854 |
< |
// if (linkLast(e)) |
855 |
< |
// modified = true; |
856 |
< |
// return modified; |
857 |
< |
// } finally { |
858 |
< |
// lock.unlock(); |
859 |
< |
// } |
860 |
< |
// } |
824 |
> |
* @param c the elements to be inserted into this deque |
825 |
> |
* @return {@code true} if this deque changed as a result of the call |
826 |
> |
* @throws NullPointerException if the specified collection or any |
827 |
> |
* of its elements are null |
828 |
> |
* @throws IllegalArgumentException if the collection is this deque |
829 |
> |
* @throws IllegalStateException if this deque is full |
830 |
> |
* @see #add(Object) |
831 |
> |
*/ |
832 |
> |
public boolean addAll(Collection<? extends E> c) { |
833 |
> |
if (c == this) |
834 |
> |
// As historically specified in AbstractQueue#addAll |
835 |
> |
throw new IllegalArgumentException(); |
836 |
> |
|
837 |
> |
// Copy c into a private chain of Nodes |
838 |
> |
Node<E> beg = null, end = null; |
839 |
> |
int n = 0; |
840 |
> |
for (E e : c) { |
841 |
> |
Objects.requireNonNull(e); |
842 |
> |
n++; |
843 |
> |
Node<E> newNode = new Node<E>(e); |
844 |
> |
if (beg == null) |
845 |
> |
beg = end = newNode; |
846 |
> |
else { |
847 |
> |
end.next = newNode; |
848 |
> |
newNode.prev = end; |
849 |
> |
end = newNode; |
850 |
> |
} |
851 |
> |
} |
852 |
> |
if (beg == null) |
853 |
> |
return false; |
854 |
> |
|
855 |
> |
// Atomically append the chain at the end |
856 |
> |
final ReentrantLock lock = this.lock; |
857 |
> |
lock.lock(); |
858 |
> |
try { |
859 |
> |
if (count + n <= capacity) { |
860 |
> |
beg.prev = last; |
861 |
> |
if (first == null) |
862 |
> |
first = beg; |
863 |
> |
else |
864 |
> |
last.next = beg; |
865 |
> |
last = end; |
866 |
> |
count += n; |
867 |
> |
notEmpty.signalAll(); |
868 |
> |
return true; |
869 |
> |
} |
870 |
> |
} finally { |
871 |
> |
// checkInvariants(); |
872 |
> |
lock.unlock(); |
873 |
> |
} |
874 |
> |
// Fall back to historic non-atomic implementation, failing |
875 |
> |
// with IllegalStateException when the capacity is exceeded. |
876 |
> |
return super.addAll(c); |
877 |
> |
} |
878 |
|
|
879 |
|
/** |
880 |
|
* Returns an array containing all of the elements in this deque, in |
1324 |
|
} |
1325 |
|
|
1326 |
|
/** |
1327 |
+ |
* @throws NullPointerException {@inheritDoc} |
1328 |
+ |
*/ |
1329 |
+ |
public boolean removeIf(Predicate<? super E> filter) { |
1330 |
+ |
Objects.requireNonNull(filter); |
1331 |
+ |
return bulkRemove(filter); |
1332 |
+ |
} |
1333 |
+ |
|
1334 |
+ |
/** |
1335 |
+ |
* @throws NullPointerException {@inheritDoc} |
1336 |
+ |
*/ |
1337 |
+ |
public boolean removeAll(Collection<?> c) { |
1338 |
+ |
Objects.requireNonNull(c); |
1339 |
+ |
return bulkRemove(e -> c.contains(e)); |
1340 |
+ |
} |
1341 |
+ |
|
1342 |
+ |
/** |
1343 |
+ |
* @throws NullPointerException {@inheritDoc} |
1344 |
+ |
*/ |
1345 |
+ |
public boolean retainAll(Collection<?> c) { |
1346 |
+ |
Objects.requireNonNull(c); |
1347 |
+ |
return bulkRemove(e -> !c.contains(e)); |
1348 |
+ |
} |
1349 |
+ |
|
1350 |
+ |
/** Implementation of bulk remove methods. */ |
1351 |
+ |
@SuppressWarnings("unchecked") |
1352 |
+ |
private boolean bulkRemove(Predicate<? super E> filter) { |
1353 |
+ |
boolean removed = false; |
1354 |
+ |
Node<E> p = null; |
1355 |
+ |
final ReentrantLock lock = this.lock; |
1356 |
+ |
Node<E>[] nodes = null; |
1357 |
+ |
int n, len = 0; |
1358 |
+ |
do { |
1359 |
+ |
// 1. Extract batch of up to 64 elements while holding the lock. |
1360 |
+ |
long deathRow = 0; // "bitset" of size 64 |
1361 |
+ |
lock.lock(); |
1362 |
+ |
try { |
1363 |
+ |
if (nodes == null) { |
1364 |
+ |
if (p == null) p = first; |
1365 |
+ |
for (Node<E> q = p; q != null; q = succ(q)) |
1366 |
+ |
if (q.item != null && ++len == 64) |
1367 |
+ |
break; |
1368 |
+ |
nodes = (Node<E>[]) new Node<?>[len]; |
1369 |
+ |
} |
1370 |
+ |
for (n = 0; p != null && n < len; p = succ(p)) |
1371 |
+ |
nodes[n++] = p; |
1372 |
+ |
} finally { |
1373 |
+ |
// checkInvariants(); |
1374 |
+ |
lock.unlock(); |
1375 |
+ |
} |
1376 |
+ |
|
1377 |
+ |
// 2. Run the filter on the elements while lock is free. |
1378 |
+ |
for (int i = 0; i < n; i++) { |
1379 |
+ |
final E e; |
1380 |
+ |
if ((e = nodes[i].item) != null && filter.test(e)) |
1381 |
+ |
deathRow |= 1L << i; |
1382 |
+ |
} |
1383 |
+ |
|
1384 |
+ |
// 3. Remove any filtered elements while holding the lock. |
1385 |
+ |
if (deathRow != 0) { |
1386 |
+ |
lock.lock(); |
1387 |
+ |
try { |
1388 |
+ |
for (int i = 0; i < n; i++) { |
1389 |
+ |
final Node<E> q; |
1390 |
+ |
if ((deathRow & (1L << i)) != 0L |
1391 |
+ |
&& (q = nodes[i]).item != null) { |
1392 |
+ |
q.item = null; |
1393 |
+ |
unlink(q); |
1394 |
+ |
removed = true; |
1395 |
+ |
} |
1396 |
+ |
} |
1397 |
+ |
} finally { |
1398 |
+ |
// checkInvariants(); |
1399 |
+ |
lock.unlock(); |
1400 |
+ |
} |
1401 |
+ |
} |
1402 |
+ |
} while (n > 0 && p != null); |
1403 |
+ |
return removed; |
1404 |
+ |
} |
1405 |
+ |
|
1406 |
+ |
/** |
1407 |
|
* Saves this deque to a stream (that is, serializes it). |
1408 |
|
* |
1409 |
|
* @param s the stream |