There is nothing wrong with the idea of modifying an element inside a list while traversing it (don't modify the list itself, that's not recommended), but it can be better expressed like this:
for (int i = 0; i < letters.size(); i++) {
letters.set(i, "D");
}
At the end the whole list will have the letter "D" as its content. It's not a good idea to use an enhanced for loop in this case, you're not using the iteration variable for anything, and besides you can't modify the list's contents using the iteration variable.
Notice that the above snippet is not modifying the list's structure - meaning: no elements are added or removed and the lists' size remains constant. Simply replacing one element by another doesn't count as a structural modification. Here's the link to the documentation quoted by @ZouZou in the comments, it states that:
Answer from Óscar López on Stack OverflowA structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification
There is nothing wrong with the idea of modifying an element inside a list while traversing it (don't modify the list itself, that's not recommended), but it can be better expressed like this:
for (int i = 0; i < letters.size(); i++) {
letters.set(i, "D");
}
At the end the whole list will have the letter "D" as its content. It's not a good idea to use an enhanced for loop in this case, you're not using the iteration variable for anything, and besides you can't modify the list's contents using the iteration variable.
Notice that the above snippet is not modifying the list's structure - meaning: no elements are added or removed and the lists' size remains constant. Simply replacing one element by another doesn't count as a structural modification. Here's the link to the documentation quoted by @ZouZou in the comments, it states that:
A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification
Use CopyOnWriteArrayList
and if you want to remove it, do the following:
for (Iterator<String> it = userList.iterator(); it.hasNext() ;)
{
if (wordsToRemove.contains(word))
{
it.remove();
}
}
There is a built in method to do exactly this operation:
Collections.replaceAll(list, foo, bar);
If you are using Java 8 with all the power of lambdas, simply do:
void
replaceAll (ArrayList<Integer> list, Integer i1, Integer i2)
{
list.replaceAll ((x) => (x.equals (i1) ? i2 : x));
}
You might also want to read about java.util.stream.Stream
The second version would be better. Internally they are the same in the end, but the second actually allows you to modify the list, while the first one will throw a ConcurrentModificationException.
But then you are using the Iterator in a wrong way. Here is how you do it correctly:
for (final ListIterator<String> i = list.listIterator(); i.hasNext();) {
final String element = i.next();
i.set(element + "yaddayadda");
}
The iterator is the one that needs to modify the list as it is the only one that knows how to do that properly without getting confused about the list elements and order.
Edit: Because I see this in all comments and the other answers:
Why you should not use list.get, list.set and list.size in a loop
There are many collections in the Java collections framework, each on optimized for specific needs. Many people use the ArrayList, which internally uses an array. This is fine as long as the amount of elements does not change much over time and has the special benefit that get, set and size are constant time operations on this specific type of list.
There are however other list types, where this is not true. For example if you have a list that constantly grows and/or shrinks, it is much better to use a LinkedList, because in contrast to the ArrayList add(element) is a constant time operation, but add(index, element), get(index) and remove(index) are not!
To get the position of the specific index, the list needs to be traversed from the first/last till the specific element is found. So if you do that in a loop, this is equal to the following pseudo-code:
for (int index = 0; index < list.size(); ++index) {
Element e = get( (for(int i = 0; i < size; ++i) { if (i == index) return element; else element = nextElement(); }) );
}
The Iterator is an abstract way to traverse a list and therefore it can ensure that the traversal is done in an optimal way for each list. Test show that there is little time difference between using an iterator and get(i) for an ArrayList, but a huge time difference (in favor for the iterator) on a LinkedList.
EDIT: If you know that size(), get(index) and set(index, value) are all constant time operations for the operations you're using (e.g. for ArrayList), I would personally just skip the iterators in this case:
for (int i = 0; i < list.size(); i++) {
list.set(i, list.get(i) + " blah");
}
Your first approach is inefficient and potentially incorrect (as indexOf may return the wrong value - it will return the first match). Your second approach is very confusing - the fact that you call next() twice and previous once makes it hard to understand in my view.
Any approach using List.set(index, value) will be inefficient for a list which doesn't have constant time indexed write access, of course. As TwoThe noted, using ListIterator.set(value) is much better. TwoThe's approach of using a ListIterator is a better general purpose approach.
That said, another alternative in many cases would be to change your design to project one list to another instead - either as a view or materially. When you're not changing the list, you don't need to worry about it.
The list is maintaining an object reference to the original value stored in the list. So when you execute this line:
Integer x = i.next();
Both x and the list are storing a reference to the same object. However, when you execute:
x = Integer.valueOf(9);
nothing has changed in the list, but x is now storing a reference to a different object. The list has not changed. You need to use some of the list manipulation methods, such as
list.set(index, Integer.valueof(9))
Note: this has nothing to do with the immutability of Integer, as others are suggesting. This is just basic Java object reference behaviour.
Here's a complete example, to help explain the point. Note that this makes use of the ListIterator class, which supports removing/setting items mid-iteration:
import java.util.*;
public class ListExample {
public static void main(String[] args) {
List<Foo> fooList = new ArrayList<Foo>();
for (int i = 0; i < 9; i++)
fooList.add(new Foo(i, i));
// Standard iterator sufficient for altering elements
Iterator<Foo> iterator = fooList.iterator();
if (iterator.hasNext()) {
Foo foo = iterator.next();
foo.x = 99;
foo.y = 42;
}
printList(fooList);
// List iterator needed for replacing elements
ListIterator<Foo> listIterator = fooList.listIterator();
if (listIterator.hasNext()) {
// Need to call next, before set.
listIterator.next();
// Replace item returned from next()
listIterator.set(new Foo(99,99));
}
printList(fooList);
}
private static void printList(List<?> list) {
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next());
}
System.out.println();
}
private static class Foo {
int x;
int y;
Foo(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return String.format("[%d, %d]", x, y);
}
}
}
This will print:
[99, 42][1, 1][2, 2][3, 3][4, 4][5, 5][6, 6][7, 7][8, 8]
[99, 99][1, 1][2, 2][3, 3][4, 4][5, 5][6, 6][7, 7][8, 8]
Use the set method to replace the old value with a new one.
list.set( 2, "New" );
Use the set method to replace the old value with a new one.
list.set( 2, "New" );
If you are unaware of the position to replace, use list iterator to find and replace element ListIterator.set(E e)
ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (next.equals("Two")) {
//Replace element
iterator.set("New");
}
}
To replace the elements in-place without building a new list,
you would need to reassign the elements at each index, that is,
essentially zorglubs[i] = zorglubs[i].transform() for each index i in the list.
A perhaps elegant way to do this is using the eachWithIndex function,
which gives you access to each element and its index:
zorglubs.eachWithIndex { it, index -> zorglubs[index] = it.transform() }
zorglubs = zorglubs.collect{it.transform()} will a bit more elegantly but actually collect returns new array.