Iterator not looping - java

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

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

Java - how to find a letter in a string in ArrayList the duplicate string

I have a problem with some tasks. I have to find words that contains letter "r" and duplicate these words. I tried to do this with for loop:
for(int i = 0; i < list.size(); i++){
if(list.get(i).contains("r")){
list.add(list.get(i));
}
But it doesnt work at all. When i add new element to array would it make it bigger? Then list.size will change and loop wont manage to get to the last element of array? Also duplicated word should be just after the original one, for example input:
show
ram
cat
output:
show
ram
ram
cat
Really i have no idea how to duplicate it.
This also doesnt work:
for(int i = 0; i < list.size(); i++){
if(list.get(i).contains("r")){
list.add(i+1, list.get(i));
}
After adding duplicate for element which contains letter r, you would eventually move to that duplicate and since it also contains r you will add duplicate for it, and then after visiting that another copy you will add another duplicate for it, and so on... infinitely so your loop will not end (until you will run of memory).
To avoid it, after duplicating element you need to jump to next element after that duplicate. You can do it by additional incrementing i after
list.add(i+1, list.get(i));
i++;
or
list.add(++i, list.get(i));
You could create a copy of your original List and add elements to it. That way the list you're iterating over doesn't change size.
For example:
List<String> list = Arrays.asList("show", "ram", "cat");
List<String> result = new ArrayList<>(list);
list.stream().filter(a -> a.contains("r")).forEach(a -> result.add(a));
Depending on the type of your list, this can easily be achieved with a ListIterator, which has an add method that adds an element exactly after the current element, but does not iterate it.
List<String> list = new ArrayList<>( Arrays.asList( "show", "ram", "cat" ) );
ListIterator<String> iterator = list.listIterator();
while ( iterator.hasNext() ) {
String value = iterator.next();
if ( value.contains("r") ) {
iterator.add(value);
}
}
System.out.println( list );
The output from this is:
[show, ram, ram, cat]
This will work with ArrayList and LinkedList, but not with the particular List that comes directly from Arrays.asList, because it is unmodifiable.
Keep it simple and just use another List<String>.
public static void main(String[] args) {
List<String> input = Arrays.asList("show", "ram", "cat");
List<String> result = new ArrayList<>();
for (String s : input) {
if (s != null && s.contains("r")) {
result.add(s);
}
result.add(s);
}
System.out.println(result);
}
Will print that you want. Hope it helps!
You need find word which contains letter r and after that add all found words to the list:
List<String> list = Arrays.asList("black", "red", "blue");
List<String> result = list.stream()
.filter(i->i.contains("r"))
.collect(Collectors.toList());
list.addAll(result);

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 hasNext means list is never empty

I just have a quick question about iterators.
I currently want to remove items that are duplicates from two lists of objects.
The way I have it set up right now is that as long as the second list (the list of objects that need to be removed from the first list) has items, the loop which has does the merging will keep on running.
I have been using the hasNext() function to check if there are still items but I think there may be a slight problem with that.
When the iterator is pointing at the last item in the list and calls hasNext(), it will return false since there is nothing after the last item. This means that the item won't be removed from the first list. Is that true?
Here's the code:
for (Iterator<Card> discardItr = discard.iterator(); discardItr.hasNext();)
{
Card tempDiscard = discardItr.next();
Iterator<Card> mixedItr = mixedHand.iterator();
while (mixedItr.hasNext())
{
if (tempDiscard.equals(mixedItr.next()))
{
discardItr.remove();
mixedItr.remove();
}
}
}
An Iterator will loop over the whole list, even when you call iterator#remove. For example running
public class IteratorDemo {
public static void main( String[] args ) {
List<String> list = new ArrayList<>( );
list.addAll( Arrays.asList("first", "second", "third" ) );
Iterator<String> iterator = list.iterator();
while ( iterator.hasNext() ) {
String next = iterator.next();
System.out.println(next);
iterator.remove();
}
}
}
produces the following output
first
second
third
So your code will work (which you would of course have discovered by just trying it)
If your dataset is small, and you can use Set instead of List, you can use following code, which is much simpler and cleaner, but needs more memory:
Set<Card> discardCopy = new HashSet<Card>(discard);
Set<Card> mixedCopy = new HashSet<Card>(mixedHand);
mixedHand.removeAll(discardCopy);
discard.removeAll(mixedCopy);

LinkedList: remove an object

Is this a valid way to find and remove item from a LinkedList in Java using a for each loop, is it possible that inconsistency may arise:
for(ObjectType ob : obList) {
if(ob.getId() == id) {
obList.remove(ob);
break;
}
}
Others have mentioned the valid point that normally this is not how you remove an object from a collection. HOWEVER, in this case it's fine since you break out of the loop once you remove.
If you want to keep iterating after a remove, though, you need to use an iterator. Otherwise you'll get a ConcurrentModificationException, or in the more general case, undefined behavior.
So yes, if you break out of the foreach after you remove, you'll be fine.
To those who's saying that this will fail because you can't modify a collection in a foreach -- this is true only if you want to keep iterating. That's not the case here, so this shortcut is fine.
A ConcurrentModificationException is checked and thrown by the iterator. Here, after the remove (which qualifies as concurrent modification), you break out of the loop. The iterator doesn't even get a chance to detect it.
It may be best if you add a comment on the break, why it's absolutely necessary, etc, because if this code is later modified to continue iterating after a remove, it will fail.
I would treat this idiom similar to goto (or rather, labeled break/continue): it may seem wrong at first, but when used wisely, it makes for a cleaner code.
It is best to use an iterator and use it's remove method when searching for an object by iterating over a collection in order to remove it. This is because
The collection could be, for example, a linked list (and in your case it is) whose remove method means searching for the object all over again, which search could have O(n) complexity.
You can't continue iteration after the remove unless you use the iterator's remove method. Right now you are removing the first occurrence - in future you might need to remove all matching occurrences, in which case you then have to rewrite the loop.
I recommend, on principle, foregoing the enhanced for and using something like this instead:
for(Iterator<ObjectType> it=obList.iterator(); it.hasNext(); ) {
if(it.next().getId()==id) {
it.remove();
break;
}
}
That way you are not making assumptions about the underlying list that could change in the future.
Compare the code to remove the last entry called by the iterator remove (formatting Sun's):
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
against what remove(Object) must do:
public boolean remove(Object o) {
if (o==null) {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}
You should use iterator.remove():
Removes from the underlying collection
the last element returned by the
iterator (optional operation). This
method can be called only once per
call to next. The behavior of an
iterator is unspecified if the
underlying collection is modified
while the iteration is in progress in
any way other than by calling this
method.
Edit: Indeed, it will not fail thanks to the break. See polygenelubricant's answer for details.
However, this is dangerous way to do. To concurrently iterate and modify a collection in Java, you must use the "ListIterator" object, and use the iterator's own "add()" and "remove()" methods, and not use the ones on the collection.
You can check the java doc for the "java.util.Iterator" and "java.util.ListIterator" classes
Try something like this:
Iterator<ObjectType> iter = obList.iterator();
while (iter.hasNext()) {
ObjectType ob = iter.next();
if(ob.getId() == id) {
iter.remove();
break;
}
}
That's one of the last places where an Iterator cannot be replaced by a foreach loop.
To avoid a ConcurrentModifiationException, you could do:
final Iterator<ObjectType> i = obList.iterator();
while (i.hasNext()) {
if (i.next().getId() == id) {
i.remove();
}
}
or
for (int i = 0; i < obList.size(); i++) {
if (obList[i].getId() == id) {
obList.remove(i);
}
}
I would prefer the first. Handling indices is more errorprone and the iterator may be implemented efficiently. And the first suggestion works with Iterable while the second requires a List.
The above second loop should be changed a bit
for (int i = 0; i < obList.size(); ) {
if (obList.get(i).getId() == id) {
obList.remove(i);
continue
}
++i;
}
or
for (int i = obList.size() - 1; i >= 0; --i) {
if (obList.get(i).getId() == id) {
obList.remove(i);
}
}
A CopyOnWriteArrayList might be what you're looking for. When mutative operations are performed, a copy of the underlying array is made. This allows modification of list elements while inside a for-each loop. Remember though that this is not a linked list and can be quite inefficient.
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Main {
public static void main(String[] args) {
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add("a");
myList.add("b");
myList.add("c");
// Will print [a, b, c]
System.out.println(myList);
for (String element : myList) {
if (element.equals("a")) {
myList.remove(element);
}
}
// Will print [b, c]
System.out.println(myList);
}
}
in java8 you can just use Collection#removeIf
like this:
List<String> myList = new ArrayList<String>();
myList.removeIf(tex-> Objects.isNull(tex)); // myList.removeIf(Objects::isNull);

Categories

Resources