Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can not convert to for-each loop when collection is modified while looping #617

Open
Luro02 opened this issue Oct 9, 2024 · 3 comments
Open
Labels
bug Something isn't working false-positive A lint triggers on something that is correct.

Comments

@Luro02
Copy link
Collaborator

Luro02 commented Oct 9, 2024

Summary

For example, the following code will throw an exception:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        var list = new ArrayList<>(List.of("a", "toRemove", "b", "toRemove"));
        
        for (var e : list) {
            if (e.equals("toRemove")) {
                list.remove(e);
            }
        }
        
        System.out.println(list);
    }
}

which would be suggested with

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        var list = new ArrayList<>(List.of("a", "toRemove", "b", "toRemove"));
        
        for (int i = 0; i < list.size(); i++) {
            var e = list.get(i);
            if (e.equals("toRemove")) {
                list.remove(e);
            }
        }
        
        System.out.println(list);
    }
}

Lint Name

FOR_CAN_BE_FOREACH

Reproducer

<code>
@Luro02 Luro02 added bug Something isn't working false-positive A lint triggers on something that is correct. labels Oct 9, 2024
@lunagl
Copy link
Contributor

lunagl commented Oct 9, 2024

The original code is most likely a bug (elements will be skipped), autograder should suggest using Iterator.remove instead

@Luro02
Copy link
Collaborator Author

Luro02 commented Oct 20, 2024

Technically, the "original code" is not really the original and the suggested code isn't really suggested.

Currently it only suggests something like, "hey your for loop, should be a for-each loop" and doesn't give any detailed suggestions.

Iterator.remove is in my opinion not something that a first year student can figure out themselves, and to be honest, I don't know how that would be done with a for-each loop either?

var iterator = list.iterator();
// NOTE: this crashes when the iterator is empty
for (var value = iterator.next(); iterator.hasNext(); value = iterator.next()) {
    // call iterator.remove here when necessary
}

For now I will make an exception for any code that modifies the collection while looping. This is the way it seems to be in IntelliJ as well.

In the future one could make the above (if worth the time, I don't think collections are modified frequently in loops) into a separate check.

@lunagl
Copy link
Contributor

lunagl commented Oct 29, 2024

Iterator.remove is in my opinion not something that a first year student can figure out themselves, and to be honest, I don't know how that would be done with a for-each loop either?

Exactly, that's the problem. So when faced with the issue of removing things while iterating, students will come up with creative (but bad) solutions using regular for loops that are error prone and often simply incorrect.

This is why I would definitely include some reference to Iterator.remove (and maybe Collection.removeIf) when a modification in the loop is detected, to make this correct way of doing things discoverable by students.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working false-positive A lint triggers on something that is correct.
Projects
None yet
Development

No branches or pull requests

2 participants