Reputation: 756
I'm using the Java 8 iterator over an array list. Suppose I have an ArrayList of futures, is there any problem/recommendation between using the classic for loop vs the arraylist foreach? (Besides not being able to throw an exception from within the lambda expression)
Suppose that the futureList is being filled concurrently, what would be the best approach? Is the last foreach example prone to any issues?
If I'm not wrong, this is the Arraylist foreach source code from jdk 1.8_131:
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
These are the two alternatives on which I want advice:
public void waitForCompletion(List<Future<HttpResponse>> futureList) {
futureList.forEach(future -> {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
LOG.error("Something went wrong: ", e);
}
});
}
vs
private void waitForCompletion(List<Future<HttpResponse>> futureList) {
for(int i=0; i < futureList.size(); i++) {
try {
futureList.get(i).get();
} catch (InterruptedException | ExecutionException e) {
LOG.error("Something went wrong: ", e);
}
}
}
In which case it is not recommended to use?:
private void waitForCompletion(List<Future<HttpResponse>> futureList) {
for(Future<HttpReponse> future:futureList) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
LOG.error("Something went wrong: ", e);
}
}
}
Upvotes: 1
Views: 1640
Reputation: 30696
In your case, you can just using for-each
loop instead:
private void waitForCompletion(List<Future<HttpResponse>> futureList) {
for(Future<HttpReponse> future:futureList) {
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
LOG.error("Something went wrong: ", e);
}
}
}
Note: ArrayList#get don't support fail-fast feature, so your second approach is different with your first one.
fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
for example:
List<Integer> list = new ArrayList<Integer>();
list.add(1);
Iterator<Integer> it = list.iterator();
it.next(); // return 1;
list.add(2);// the list is modified
it.next();
// ^--- fail-fast throws a ConcurrentModificationException
the for-each
loop is equivalent to ArrayList#forEach, they are both support fail-fast feature. the different between them is for-each
loop can throws a checked Exception
directly, for example:
private void waitForCompletion(List<Future<HttpResponse>> futureList)
throws Exception {
for(Future<HttpReponse> future:futureList) {
future.get();
}
}
Another bit different is Array#forEach only create a lambda expression instance once. but for-each
loop will create a new Iterator
for each call, for example:
void forEachLoop(){
// v--- create a new Iterator when forEachLoop was called.
for(T item:items);
}
void forEach(){
// v--- create a single lambda instance.
items.forEach(it->{});
}
Upvotes: 3
Reputation: 1240
A few years ago I compiled a class containing a for each loop them decomplied it. Using jd-gui.
My loop
for (Object object: objectList) {
// Stuff
}
Decomplied to
for (into i =0 ; i < objectList.lenght(); i ++) {
Object object = objectList.get(i);
// Stuff
}
So they are basically equivalents (or at least they were in java 5)
Upvotes: 0
Reputation: 23
You need to use a synchronized list for your purpose:
List<Future<HttpResponse>> objList =
Collections.synchronizedList(new ArrayList<Future<HttpResponse>>());
When performing concurrent thread operations, the access to each independent process must be thread-safe. You are building a thread-execution service, and you will not run the risk that two future-tasks update the list simultaneously, e.g. through their return objects.
Alternative
Note further that you could also configure a threadpool of 'Runnable' Java classes, as elements of your list. Here is an implementation that I have actual production experience with (based on ConcurrentHashMap
):
Map<ReturnData, Map<String, String>> returnMap = new ConcurrentHashMap<>();
ExecutorCompletionService<Map<ReturnData, Map<String, String>>> exService = new ExecutorCompletionService<>(executorServiceGetReturnData);
List<Callable<Map<ReturnData, Map<String, String>>>> callMethods = new ArrayList<>(ReturnData.size());
Upvotes: 0
Reputation: 1936
Cost wise I believe they are almost identical--just depends what is going on behind the scenes and what you chose to write yourself. Readability-wise, it is usually nicer to use the for-each style loops due to the easy reference of individual objects in a list.
Upvotes: 0