Iterator cannot iterative again - java

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.

Related

How does an Iterator work on a LinkedList? [duplicate]

If I run the following code, it will print out 3 times duplicate, but when I remove the if statement inside the while loop (just to see how many times it will iterate) it starts an infinite loop.
How does actually this hasNext() method working? I thought that will iterate only 5 times as I have 5 items in the list.
public class ExerciseOne {
public static void main(String []args){
String []colors = {"MAGENTA","RED","WHITE","BLUE","CYAN"};
List<String> list = new ArrayList<String>();
for(String color : colors)
list.add(color);
String[] removeColors = {"RED","WHITE","BLUE"};
List<String> removeList = new ArrayList<String>();
for(String color : removeColors)
removeList.add(color);
removeColors(list,removeList);
System.out.printf("%n%nArrayList after calling removeColors:%n");
for(String color : list)
{
System.out.printf("%s ",color);
}
}
private static void removeColors(Collection<String> collection1, Collection<String> collection2)
{
Iterator<String> iterator = collection1.iterator();
while(iterator.hasNext()){
if(collection2.contains(iterator.next()))
System.out.println("duplicate");
}
}
}
It is pretty simple, actually
while(iterator.hasNext()){
if(collection2.contains(iterator.next()))
System.out.println("duplicate");
}
Imagine that the iterator is a pointer to an element of your list.
When you call next(), you're moving this pointer one step ahead.
If you don't move the pointer, hasNext() will always be true because you're still in the beginning of the list.
So you have to call the iterator's next() until there isn't any remaining element in the list.
If you remove the if statement, then it will go for an infinite loop since your iterator.next() is in the if condition. Actually iterator.next() is the api that moves the pointer, not the hasNext(). hasNext() just checks if there is any element in the collection. Since removal of the if statement is also removing the hasNext api, the pointer to the collection is not moving and hasNext is always returning true.
If you take out the iterator.next() from the if condition and move it above the if condition, then the loop will iterate for 5 times even after you remove the if statement.
Iterator<String> iterator = collection1.iterator();
while(iterator.hasNext()){
String currentColor = iterator.next();
if(collection2.contains(currentColor)){
System.out.println("duplicate");
}
}
The question why Iterator is important/introduced is simple:
consider following example:
List<String> list = new ArrayList<String>();
list.add("Anurag");
list.add("Soni");
list.add("MMM");
list.add("GKP");
for(string s : list){
if(s.equals(" Anurag")
s.remove();
System.out.println(s);
}
This will throw an exception-`Concurrent Modification exception` as you are trying to alter the structure of the data structure List before the iteration is completed.
so you may use Iterator for the same purpose .
Iterator iterator = List.iterator();
while(iterator.hasNext()){
String current = iterator.next();
if(current=="Anurag"){
iterator.remove();
}else{
System.out.println(current);
}
}
OUTPUT: Soni
MMM
GKP

enhanced for-loop behavior for handling collection object

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

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());
}

Iterator not looping

In my project, the user inputs random letters. Then I iterate through my_list to see if any of those random letters appear in my_list. If so, I remove them from my_list.
Example:
List<String> my_list contains: [a, b, c, d]
List<String> rand contains: [r, a]
Goal: The a will be removed from my_list
Problem: The Iterator loops through my_list searching for the letter r. The letter r is not in my_list. But instead of continuing to the next letter a, the iterator exits the loop and the a still remains in my_list
Can someone please tell me why my loop keeps breaking after the first letter?
Here's my code:
public void removeLetters( List<String> my_list, List<String> rand ) {
Iterator<String> i = my_list.iterator();
for( String s : rand ) {
while( i.hasNext() ) {
Object o = i.next();
if( o.toString().equals( s ) ) {
i.remove();
i = my_list.iterator();
break;
}
}
}
}
I hope I explained my problem good enough. Please let me know if I need to explain more in detail.
Thanks
Try creating the Iterator inside the for loop.
for(String s : rand) {
Iterator<String> i = my_list.iterator();
while(i.hasNext()) {
...
}
}
The problem is that in your inner loop you reach the end of i iterator with the first iteration of the outer loop. Then when the second iteration of the for begin, the iterator i hasNext method always return false and it seems that nothing is done, like you said.
You should re initialize your i iterator for each new s of your for
i.remove();
here is your problem. you must remove it from my_list as you explained.
so use
my_list.remove();
You need to reset the Iterator to its beginning before looping through for the next item.
You can only go through an Iterator once. Think about it: once i.hasNext() returns false, you fall out of the inner loop, loop around the outer one, and call i.hasNext() again. Why would it start returning true now?
Bottom line: you have to create a new iterator every time you restart the outer loop (basically move my_list.iterator() call inside the loop)
Better yet, iterate through the list once, and use contains() on the input list:
for(Iterator it = my_list.iterator(); it.hasNext(); ) {
if(rand.contains(it.Next()) {
it.remove();
}
}
If the input list (rand) could be large enough for this to matter, you might want to convert it into a Set before the loop: rand = new HashSet(rand); this will make you algorithm linear rather than quadratic.
Or, just use the one liner:
my_list.removeAll(rand);
You could also use the List's ability to remove objects by equality:
for(String s: rand) {
my_list.remove(s);
}
If you do want to iterate, this is exactly what java.util.AbstractCollection does:
Iterator<String> it = my_list.iterator();
while (it.hasNext()) {
if (rand.contains(it.next())) {
it.remove();
}
}
IS there a reason why you just don't use contains?
public void removeLetters(List<String> my_list, List<String> rand)
{
List<String> updatedList = new ArrayList<String>();
for (String s : my_list)
{
if (!rand.contains(s))
{
updatedList.add(s);
}
}
}

How to get the current loop index when using Iterator?

I am using an Iterator to iterate through a collection
and I want to get the current element's index.
How can I do that?
I had the same question and found using a ListIterator worked. Similar to the test above:
List<String> list = Arrays.asList("zero", "one", "two");
ListIterator<String> iter = list.listIterator();
while (iter.hasNext()) {
System.out.println("index: " + iter.nextIndex() + " value: " + iter.next());
}
Make sure you call the nextIndex() before you actually get the next().
Use your own variable and increment it in the loop.
Here's a way to do it using your own variable and keeping it concise:
List<String> list = Arrays.asList("zero", "one", "two");
int i = 0;
for (Iterator<String> it = list.iterator(); it.hasNext(); i++) {
String s = it.next();
System.out.println(i + ": " + s);
}
Output (you guessed it):
0: zero
1: one
2: two
The advantage is that you don't increment your index within the loop (although you need to be careful to only call Iterator#next once per loop - just do it at the top).
You can use ListIterator to do the counting:
final List<String> list = Arrays.asList("zero", "one", "two", "three");
for (final ListIterator<String> it = list.listIterator(); it.hasNext();) {
final String s = it.next();
System.out.println(it.previousIndex() + ": " + s);
}
What kind of collection? If it's an implementation of the List interface then you could just use it.nextIndex() - 1.
Use an int and increment it within your loop.
just do something like this:
ListIterator<String> it = list1.listIterator();
int index = -1;
while (it.hasNext()) {
index++;
String value = it.next();
//At this point the index can be checked for the current element.
}
Use a ListIterator to iterate through the Collection. If the Collection is not a List to start with use Arrays.asList(Collection.toArray()) to turn it into a List first.
All you need to use it the iterator.nextIndex() to return the current index that the iterator is on. This could be a bit easier than using your own counter variable (which still works also).
public static void main(String[] args) {
String[] str1 = {"list item 1", "list item 2", "list item 3", "list item 4"};
List<String> list1 = new ArrayList<String>(Arrays.asList(str1));
ListIterator<String> it = list1.listIterator();
int x = 0;
//The iterator.nextIndex() will return the index for you.
while(it.hasNext()){
int i = it.nextIndex();
System.out.println(it.next() + " is at index" + i);
}
}
This code will go through the list1 list one item at a time and print the item's text, then "is at index" then it will print the index that the iterator found it at. :)
See here.
iterator.nextIndex() would provide index of element that would be returned by subsequent call to next().
Though you already had the answer, thought to add some info.
As you mentioned Collections explicitly, you can't use listIterator to get the index for all types of collections.
List interfaces - ArrayList, LinkedList, Vector and Stack.
Has both iterator() and listIterator()
Set interfaces - HashSet, LinkedHashSet, TreeSet and EnumSet.
Has only iterator()
Map interfaces - HashMap, LinkedHashMap, TreeMap and IdentityHashMap
Has no iterators, but can be iterated using through the keySet() / values() or entrySet() as keySet() and entrySet() returns Set and values() returns Collection.
So its better to use iterators() with continuous increment of a value to get the current index for any collection type.

Categories

Resources