3

I found this thread on the topic and I wondered why it's so hard in Java to replace a value in a List.

My code looks ugly like this:

    List<String> stringList = new ArrayList<>();
    // ... add some elements
    for (int i = 0; i< stringList.size(); ++i) {
        if (stringList.get(i).contains("%")) {
            stringList.set(i, stringList.get(i).replace("%", backupStorePath));
        }
    }

Is this really the only way to do this? Why can't I use a foreach loop?

for (String command : stringList) {
    command = command.replace("%", backupStorePath);
}

This must be a java's "Copy by value" problem and String being immutable, but why was it implemented like that? Why is command a copy of the original reference and not the original itself?

Community
  • 1
  • 1
codepleb
  • 10,086
  • 14
  • 69
  • 111
  • 2
    Immutability is not the issue here. `command` is a variable that references to the n-th element of the list, but assigning a different value to this variable just causes it to reference something else, without any effect on the list. – Erich Kitzmueller Mar 18 '16 at 06:05

3 Answers3

5

Since Java-8 you have a new option to use List.replaceAll:

stringList.replaceAll(command -> command.replace("%", backupStorePath));

Note that for loop works for any Iterable, not only List. And most of other iterables do not support the element replacement. So even if Java designers had decided to support modifications, it would be necessary to separate List and non-List cases which would certainly add some complexity.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • This was the solution for my problem, thank you. But Eliotts answer fits better to the question. Thank you very much for this input! :) – codepleb Mar 18 '16 at 08:20
3

From Java Programming Language: The For-Each Loop (emphasis added)

The for-each loop hides the iterator, so you cannot call remove. Therefore, the for-each loop is not usable for filtering. Similarly it is not usable for loops where you need to replace elements in a list or array as you traverse it. Finally, it is not usable for loops that must iterate over multiple collections in parallel. These shortcomings were known by the designers, who made a conscious decision to go with a clean, simple construct that would cover the great majority of cases.

You could use an Iterator and an index. Something like

// ... add some elements
Iterator<String> stringIter = stringList.iterator();
int i = 0;
while (stringIter.hasNext()) {
    String command = stringIter.next();
    if (command.contains("%")) {
        command = command.replace("%", backupStorePath);
        stringList.set(i, command);
    }
    i++;
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
0

it will like be this:

List list1 = new ArrayList();
List list 2 = list1;
list2 = new LinkedList();  //this just make list2 to point to an new Object no reference about list1.

so your code :

command = command.replace("%", backupStorePath);  //this just make command to a new point of memory not have any chang about your list
Timi
  • 892
  • 1
  • 8
  • 17