enhanced for-loop behavior for handling collection object - java

As I am learner of Java.. I came across the following code
public static void main(String[] args) {
ArrayList<String> a = new ArrayList<>();
a.add("1");
a.add("2");
for(String str: a){
a = new ArrayList<>();
System.out.println(str);
}
}
I guessed the answer to be
1
null (since the reference is now pointing another object)
but the answer is
1
2
I am unable understand the behavior of enhanced for loop here.

The enhanced for loop creates an Iterator to iterate of the elements of your ArrayList. Changing the a reference to refer to a new ArrayList doesn't affect the Iterator that was created by the loop.
Your loop is equivalent to
Iterator<String> iter = a.iterator();
while (iter.hasNext()) {
String str = iter.next();
a = new ArrayList<>();
System.out.println(str);
}

When you run
for(String str: a)
It gets an iterator from a, then iterates using that iterator. Reassigning a after it has the iterator will have no effect since it isn't using the a reference, it's using the iterator that a returned when the loop started.

This is because, enhanced for loop uses iterator. So changing the reference will not have any impact.
You can check different scenarios here

Related

Collision Detection returns ConcurrentModificationException [duplicate]

This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 8 years ago.
I'm trying to remove some elements from an ArrayList while iterating it like this:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Of course, I get a ConcurrentModificationException when trying to remove items from the list at the same time when iterating myArrayList. Is there some simple solution to solve this problem?
Use an Iterator and call remove():
Iterator<String> iter = myArrayList.iterator();
while (iter.hasNext()) {
String str = iter.next();
if (someCondition)
iter.remove();
}
As an alternative to everyone else's answers I've always done something like this:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
This will avoid you having to deal with the iterator directly, but requires another list. I've always preferred this route for whatever reason.
Java 8 user can do that: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
It will remove elements in the list, for which someCondition is satisfied
You have to use the iterator's remove() method, which means no enhanced for loop:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
No, no, NO!
In single threated tasks you don't need to use Iterator, moreover, CopyOnWriteArrayList (due to performance hit).
Solution is much simpler: try to use canonical for loop instead of for-each loop.
According to Java copyright owners (some years ago Sun, now Oracle) for-each loop guide, it uses iterator to walk through collection and just hides it to make code looks better. But, unfortunately as we can see, it produced more problems than profits, otherwise this topic would not arise.
For example, this code will lead to java.util.ConcurrentModificationException when entering next iteration on modified ArrayList:
// process collection
for (SomeClass currElement: testList) {
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
}
}
But following code works just fine:
// process collection
for (int i = 0; i < testList.size(); i++) {
SomeClass currElement = testList.get(i);
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
i--; //to avoid skipping of shifted element
}
}
So, try to use indexing approach for iterating over collections and avoid for-each loop, as they are not equivalent!
For-each loop uses some internal iterators, which check collection modification and throw ConcurrentModificationException exception. To confirm this, take a closer look at the printed stack trace when using first example that I've posted:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at TestFail.main(TestFail.java:43)
For multithreading use corresponding multitask approaches (like synchronized keyword).
While other suggested solutions work, If you really want the solution to be made thread safe you should replace ArrayList with CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception
List<String> s = new CopyOnWriteArrayList<>();
s.add("B");
Iterator<String> it = s.iterator();
s.add("A");
//Below removes only "B" from List
while (it.hasNext()) {
s.remove(it.next());
}
System.out.println(s);
If you want to modify your List during traversal, then you need to use the Iterator. And then you can use iterator.remove() to remove the elements during traversal.
List myArrayList = Collections.synchronizedList(new ArrayList());
//add your elements
myArrayList.add();
myArrayList.add();
myArrayList.add();
synchronized(myArrayList) {
Iterator i = myArrayList.iterator();
while (i.hasNext()){
Object object = i.next();
}
}
One alternative method is convert your List to array, iterate them and remove them directly from the List based on your logic.
List<String> myList = new ArrayList<String>(); // You can use either list or set
myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");
Object[] obj = myList.toArray();
for(Object o:obj) {
if(condition)
myList.remove(o.toString());
}
You can use the iterator remove() function to remove the object from underlying collection object. But in this case you can remove the same object and not any other object from the list.
from here

Simple loop not stopping on String Array

Being new to Java, the following code confuses me. I'm trying to do a simple loop based on a list.
List memberships = getMembership(username);
for( Iterator<Integer> single = memberships.iterator(); single.hasNext(); )
{
System.out.println(print_the_current_string_in_list);
}
I have the following questions:
The loop never stops with the above, even though I only have three items in the list. Why is this?
How can I output the current string in the list?
Hopefully your List if of type String and not Integer so it should be
List<String> memberships = getMembership(username);
There are multiple ways to loop over the data for example:
for(String single : memberships) {
System.out.println(single);
}
Another way:
for(int i = 0; i < memberships.size(); i++) {
System.out.println(memberships.get(i));
}
Using the Iterator
for(Iterator<String> iterator = membership.iterator(); iterator.hasNext();) {
System.out.println(iterator.next());
}
Instead of using a for loop you may use an Iterator with a while loop
Iterator<String> iterator = membership.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
Try an advanced for loop, like so:
for(String membership : memberships)
System.out.println(membership);
You can use the foreach statement, which uses the iterator that the list has already implemented.
And you should also specify the type of the elements that the list will contain in the List declaration, like this:
List<String> memberships = getMembership(username);
for( String m : memberships )
{
System.out.println(m);
}
You need to actually iterate over the members with next() method from Iterator:
for (Iterator<Integer> single = memberships.iterator (); single.hasNext (); ) {
Integer next = single.next ();
System.out.println (next);
}
Also, you can use the new for each construct:
for (Integer current : memberships)
System.out.println (current);
You haven't iterated through the list.Use next() method.Thats why its going into an infinite loop.
for( Iterator<Integer> single = memberships.iterator(); single.hasNext();{
System.out.println( single.next());
}

Java for each loop working

I was working on certain task, when incidentally did something wrong according to me but the code executed and provided correct result. I was little surprised and had question in mind how all these for each loop works.
Example (sample program),
public static void main( String[] args )
{
String myInput = "hello , hi , how are you ";
String[] splitted = myInput.split(",");
List<String> mylist = new ArrayList<String>();
for (String output : splitted)
{
mylist.add(output);
}
for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
for (String output : splitted)
{
mylist.add(output);
}
for (String output : mylist)
{
System.out.println(output);
mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
}
}
I was curious to know and while searching I found one more post that said we can remove elements from list if we used iterator approach, So I tried,
for (String output : splitted)
{
mylist.add(output);
}
for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
String string = (String) iterator2.next();
System.out.println(string);
iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}
Now I just want to know what is happening behind the every for each loop quoted above.
I want to know the technical aspect, I know I can not modify the collection in for each loop but in some case stated above it worked why?
Now I just want to know what is happening behind the every for each
loop quoted above
1. for (String output : splitted)
{
mylist.add(output);
}
This adds each output String from splitted array to the mylist list.
2. for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
The for statement is governed by the following production:
for ( FormalParameter : Expression )
Statement
where Expression must be an instance of java.lang.Iterable, or an array. So this for:each loop is equivalent to this:
Iterator<String> iterator = mylist.iterator();
while (iterator.hasNext()) {
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
Here mylist.iterator() will return a new instance of Iterator type:
public Iterator<E> iterator() {
return new Itr();
}
So even if you are creating new ArrayList instances and assigning them to mylist on each iteration, the iterator obtained from the original mylist will still have a reference to the original mylist and will keep iterating through the elements of original mylist. The iterator keeps a reference to the list it was created on. The assignment mylist = new ArrayList<String>() has no effect on the data that the iterator works on because it changes the variable mylist and not the list itself.
3. for (String output : mylist)
{
System.out.println(output);
mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
}
Below statement explains this behavior. It is copied from Arraylist doc:
The iterators returned by this class's iterator and listIterator methods are 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.
4. for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
String string = (String) iterator2.next();
System.out.println(string);
iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}
The above statement also explains the behavior of this for loop: the list can be structurally modified by the iterator's own remove or add methods while iterating through the list.
A for-each loop is possible for Classes that implement Iterable. This also means that you can create Classes yourself which you can use in for-each loops, which can be very comfortable.
This interface forces you to implement a method iterator() which returns an Iterator. Then the for-each loop does nothing but retrieve that iterator and iterate over it using hasNext() and next(). Just the same as you would do it yourself.
The problem with removing is that when you use a for-each loop and then remove an element from the List, the constructed Iterator will not know anything about that change and there will be a ConcurrentModificationException.
But if you call Iterator.remove() directly, the Iterator will know about that change and can handle it.
A common little trick to avoid Iterators and Exceptions at the same time is to do something like this:
List<Object> objects = new ArrayList<Object>();
for (Object object : new ArrayList<Object>(objects)) {
objects.remove(object);
}
So you create a temporary copy of that List, iterate over that, but call remove on the original List.
for-each loop of List will be internally converted to for loop with iterator.
for (String output : mylist)
{
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
gets converted to
for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
String output = (String)iterator.next();
System.out.println(output);
mylist = new ArrayList<String>(); //It worked
mylist.add(output);
}
And since the the snapshot of list is already taken at below
for (Iterator<String> iterator = mylist.iterator(); iterator.hasNext();) {
The loop is running until the last element of list i.e. "how are you".
Whereas, below is not working because of FailFast behaviour of List.
for (String output : mylist)
{
System.out.println(output);
mylist.add(output); // After this line it threw exception java.util.ConcurrentModificationException
}
It says, if you are modifying the list while iterating, with anything other than iterator's own remove method, List will throw ConcurrentModificationException and thats the reason the below is working.
for (Iterator iterator2 = mylist.iterator(); iterator2.hasNext();)
{
String string = (String) iterator2.next();
System.out.println(string);
iterator2.remove(); //It worked but if I used the same thing to remove element from original list it threw exception.
}
That is corrent. You cannot modify value of collection that is beeing iterated over using "foreach" loop, and to do that, you have to use collection's iterator.
It is of course not a problem to add something to a completely different list than the one you are currently traversing, as you did with the line mylist = new ArrayList<String>(); Even though the variable still has the same name, it will point to an entirely different list.
The reason why you cannot add something to a list that is currently being "walked through" is, that the internal implementation of that list might not be able to ensure, that you still get the same order of elements and especially not all remaining elements as you would expect.
This can be understand best if you imagine that you are using a sorted list: you put in a new element, but whether or not you see that element is undefined, as it depends on where you are and what you insert. As Java doesn't know if you are ok with that, it takes the safe road and throws an Exception.
There are however lists that are well capable of being able to be modified during traversal, mostly the concurrent lists in the concurrent package.

Iterator cannot iterative again

Here is my Java code:
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("_name", "name");
map.put("_age", "age");
Set<String> set = map.keySet();
Iterator iterator = set.iterator();
// the first iteration
StringBuffer str1 = new StringBuffer();
while (iterator.hasNext()) {
str1.append(iterator.next() + ",");
}
String str1To = str1.substring(0, str1.lastIndexOf(",")).toString();
System.out.println(str1To);
// the second iteration
StringBuffer str2 = new StringBuffer();
while (iterator.hasNext()) {
str2.append(iterator.next() + ",");
}
String str2To = str2.substring(0, str2.lastIndexOf(",")).toString();// ?????
System.out.println(str2To);
}
My question is,why doesn't the second loop iterate? Does the first iteration already takes the iterator to the end? Is this what affects the second iteration?
How do I fix it?
Your first while loop would move iterate till the iterator reaches the end of the list. At that moment the iterator in itself is pointing to the end of the list, in your case the map.keySet(). And that is the reason why your next while loop fails because the call to the iterator.hasNext() returns false.
A better way would be to use the Enhanced For Loop, something like this instead of your while loops:
for(String key: map.keySet()){
//your logic
}
Iterator's are for one time use only. So ask for iterator again.
You need to call set.iterator() each time you want to iterate through a collection. I suggest that you use a different variable for each iteration as well.

Can we use for-each loop for iterating the objects of Iterator type? [duplicate]

This question already has answers here:
Idiomatic way to use for-each loop given an iterator?
(9 answers)
Closed 3 years ago.
If we do the following we get error:
class FGH{
public static Iterator reverse(List list) {
Collections.reverse(list);
return list.iterator();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
/*for(Iterator it:reverse(list))
Iterator it=reverse(list);*/
for (Object obj: reverse(list))
System.out.print(obj + ", ");}}
but if we modify the code like this we don't get error,so is it mean that we can't iterate the objects of Iterator type? :
class FGH{
public static Iterator reverse(List list) {
Collections.reverse(list);
return list.iterator();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
Iterator it=reverse(list);
while(it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}
}}
A lot of answers talk about how iterators are not iterables. That's true, but such an answer doesn't touch on why.
The reason for-each loops require an iterable is to allow the same object to be traversed multiple times (so that you can use multiple for-each loops over the same object without surprising behaviour), whereas an iterator only allows one traversal. If for-each allowed iterators to be used, the behaviour would be surprising to programmers who didn't realise that their iterators would be exhausted after the loop is run.
If you're using an API that only gives you iterators, and you want to use iterables, you have two ways to solve this:
Make an anonymous iterable class, whose iterator() method calls the API function that returns the iterator. That way, each time you use a for-each loop on the iterable object, that API function is called again, returning a new (and unexhausted) iterator.
Make a one-pass iterable wrapper class that takes an iterator and allows one call to iterator(). On subsequent calls, throw an AssertionError or IllegalStateException as appropriate.
The for loop in your first example expects that reverse(list) is a collection of Iterator, which of course it isn't. That's why that example won't work.
In general, you can only use foreach on classes that implement Iterable. Iterator is not one of these classes.
See http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Iterable.html
You would have to wrap your Iterator in an Iterable first. Here's a Java 8 solution:
for (Object obj : (Iterable<Object>) () -> reverse(list))
System.out.print(obj + ", ");
Or, just use Iterator.forEachRemaining():
reverse(list).forEachRemaining(obj -> System.out.print(obj + ", "));
You'd use the enhanced for loop in this way:
for (Object o : list)
As pointed out above, you need to reference an Iterable (or array) in a for-each block. For example, when you do the following:
Collection<String> coll = ...;
for (String str : coll) {
...
}
The following is really happening:
Collection<String> coll = ...;
for (Iterator<String> iter = coll.iterator(); iter.hasNext(); ) {
String str = iter.next();
}
In order to be able to do this, the argument has to implement Iterable (e.g. have a public iterator() method that returns an Iterator (or be an array). So your code should look like the following:
class FGH {
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
while (Object obj : Collections.reverse(list)){
System.out.println(obj);
}
}
}
The "for each" syntax is designed for collections (Iterable to be precise), not for Iterators. I'm trying to find out the arguments of why Java was desinged this way in this question.
The simplest solution for you would be to return the reversed list (which is iterable) instead of a list iterator. Then you can use the shorthand for loop syntax.
A hackish way is outlined in the question mentioned earlier, using an Adapter class that wraps the Iterator as Iterable, and returns the Iterator on the first invocation of iterator(). Look at the adapter code, it's fairly simple. It serves the purpose, but since the iterator can only be used once, it is somewhat an invalid Iterable (it will not be able produce a second iterator).
The key behavior difference shows up when you use it twice:
for(Object a : foo) { }
for(Object a : foo) { }
will process all elements in foo twice if it is a proper Iterable, but only once if it is using the adapter I sketched - the second loop will do nothing.

Categories

Resources