ArrayList algorithms: filter and the remove bug · Algorithmes ArrayList : filtrage et le bug remove
Doing work on a whole list
- Now you can store data in an
ArrayList. Next we process it. - Common jobs: count items that match a rule, filter them into a new list, and remove some of them.
- All of these use a loop over the list.
Travailler sur toute une liste
- Maintenant, vous pouvez stocker des données dans un
ArrayList. La prochaine étape consiste à les traiter. - Tâches courantes : compter les éléments qui correspondent à une règle, les filtrer dans une nouvelle liste, et supprimer certains d'entre eux.
- Toutes ces tâches utilisent une boucle sur la liste.
Counting with a rule
- Walk the list, test each item, and add
1to a counter when the test is true. n % 2 == 0is true whennis even.n % 2 != 0is true whennis odd.- This only reads the list, so an enhanced for-loop is fine.
Compter selon une règle
- Parcourir la liste, tester chaque élément, et ajouter
1à un compteur lorsque le test est vrai. n % 2 == 0est vrai sinest pair.n % 2 != 0est vrai sinest impair.- Cela ne fait que lire la liste, donc une boucle for améliorée suffit.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> nums = new ArrayList<Integer>();
nums.add(4);
nums.add(7);
nums.add(10);
nums.add(3);
int evens = 0;
for (int x : nums) {
if (x % 2 == 0) {
evens = evens + 1;
}
}
System.out.println(evens); // 2
}
}
Filtering into a new list
- Filtering keeps only the items that pass a test.
- A safe pattern: make an empty new list, then
addeach item that passes. - The old list is not changed. This avoids the bug we see next.
Filtrer dans une nouvelle liste
- Le filtrage conserve uniquement les éléments qui passent le test.
- Pattern sûr : créer une nouvelle liste vide, puis
addchaque élément qui passe. - L'ancienne liste n'est pas modifiée. Cela évite le bug que nous verrons ci-dessous.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> nums = new ArrayList<Integer>();
nums.add(4);
nums.add(7);
nums.add(10);
nums.add(3);
ArrayList<Integer> bigOnes = new ArrayList<Integer>();
for (int x : nums) {
if (x >= 5) {
bigOnes.add(x);
}
}
System.out.println(bigOnes); // [7, 10]
}
}
The remove-while-iterating bug
- It looks easy: loop forward and
remove(i)the items you do not want. - But
remove(i)shifts every later item one place left. The loop then adds1toiand skips the item that moved into the old spot. - The example below tries to remove all evens but misses one.
Le bug de suppression lors de l'itération
- Cela semble facile : boucler vers l'avant et
remove(i)les éléments que vous ne voulez pas. - Mais
remove(i)décale tous les éléments suivants d'une place vers la gauche. La boucle incrémente ensuite1versiet saute l'élément qui s'est déplacé dans l'ancien emplacement. - L'exemple ci-dessous tente de supprimer tous les pairs mais en manque un.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> nums = new ArrayList<Integer>();
nums.add(2);
nums.add(4); // this one gets skipped!
nums.add(5);
// BUGGY: forward loop while removing
for (int i = 0; i < nums.size(); i++) {
if (nums.get(i) % 2 == 0) {
nums.remove(i);
}
}
System.out.println(nums); // [4, 5] -- wrong, 4 was missed
}
}
Why it skips
- Start:
[2, 4, 5],i = 0.2is even, remove it. List becomes[4, 5]. - The
4slid down into index0. But the loop now setsi = 1. - At
i = 1we look at5, not4. The4was never checked. It stays in.
Pourquoi cela saute-t-il ?
- Départ :
[2, 4, 5],i = 0.2est pair, on le supprime. La liste devient[4, 5]. - Le
4a glissé dans l'index0. Mais la boucle définit maintenanti = 1. - À
i = 1, on regarde5, pas4. Le4n'a jamais été vérifié. Il reste dans la liste.
The fix: loop backwards
- Walk from the last index down to
0. - When you remove index
i, only items afterishift — and you have already passed those. - The indexes you still need to visit are smaller than
i, so nothing moves out from under you.
La solution : boucler vers l'arrière
- Parcourir depuis l'index le plus grand jusqu'à
0. - Lorsque vous supprimez l'index
i, seuls les éléments situés aprèsise déplacent — et vous avez déjà traité ceux-là. - Les indices que vous devez encore visiter sont plus petits que
i, donc rien ne se décale sous vous.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> nums = new ArrayList<Integer>();
nums.add(2);
nums.add(4);
nums.add(5);
// SAFE: backward loop while removing
for (int i = nums.size() - 1; i >= 0; i--) {
if (nums.get(i) % 2 == 0) {
nums.remove(i);
}
}
System.out.println(nums); // [5] -- correct
}
}
A note on the enhanced for-loop
- You may be tempted to remove inside
for (int x : nums). - Do not. Changing the list size during an enhanced for-loop throws a
ConcurrentModificationExceptionand stops the program. - For removing, always use the backward index loop above.
Remarque sur la boucle for améliorée
- Vous serez tenté de supprimer à l'intérieur de
for (int x : nums). - Ne le faites pas. La modification de la taille de la liste pendant une boucle for améliorée lève une
ConcurrentModificationExceptionet arrête le programme. - Pour supprimer, utilisez toujours la boucle à index inverse ci-dessus.
Common mistakes
- Removing while looping forward by index skips the next item (the remove bug).
- Loop backwards, or use an iterator, when removing.
Erreurs courantes
- Supprimer tout en bouclant vers l'avant par index saute l'élément suivant (le bug de suppression).
- Boucler vers l'arrière, ou utiliser un itérateur, pour supprimer.
Now you try
- Each task pre-fills the class skeleton — write your code inside main, or complete the method shown.
- Press Run to compile and run, then Check answer.
- Your code compiles and runs on the server, so even the first run is fast.
À vous maintenant
- Chaque tâche préremplit le squelette de classe — écrivez votre code dans main, ou complétez la méthode montrée.
- Appuyez sur Exécuter pour compiler et exécuter, puis sur Vérifier la réponse.
- Votre code compile et s'exécute sur le serveur, donc même la première exécution est rapide.
Filtering a list · Filtrage d'une liste
Removing while looping shifts indices — step through to see why. · Supprimer pendant la boucle décale les indices — faites-le étape par étape pour voir pourquoi.
Complete countOdds(ArrayList<Integer> a) so it returns how many numbers in the list are odd. A number is odd when x % 2 != 0. · Complétez countOdds(ArrayList<Integer> a) pour qu'elle retourne combien de nombres de la liste sont impairs. Un nombre est impair quand x % 2 != 0.
Click Run to see the output here. · Cliquez sur Exécuter pour voir le résultat ici.
Complete keepPositives(ArrayList<Integer> a). Build and return a new ArrayList<Integer> that holds only the numbers greater than 0, in their original order. Do not · non change · changement a. · Complétez keepPositives(ArrayList<Integer> a). Construisez et retournez un nouveau ArrayList<Integer> qui contient uniquement les nombres supérieurs à 0, dans leur ordre d'origine. Ne modifiez pas a.
Click Run to see the output here. · Cliquez sur Exécuter pour voir le résultat ici.
Complete removeEvens(ArrayList<Integer> a) so it removes every even number from a itself. Loop backwards by index so you do not skip items. Return nothing (void). · Complétez removeEvens(ArrayList<Integer> a) pour qu'elle supprime tous les nombres pairs de a lui-même. Bouclez vers l'arrière par indice pour ne pas sauter d'éléments. Ne retournez rien (void).
Click Run to see the output here. · Cliquez sur Exécuter pour voir le résultat ici.