简体   繁体   中英

Modifying elements while iterating Java Set

Pretty new to Java here. I am coming from Python. There are similar questions on SO talking about remove or add element while iterating a Java Set. What I would like to know is to modify the elements containing in the Set. For instance, ["apple", "orange"] to ["iapple", "iorange"] . In addition, I would like to do it in place, ie, not creating another set and put the modified element into the new set while iterating it.

Apparently a simple for loop doesn't work, as the following:

import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;

class Test {
    public static void main (String[] args) {
        Set<String> strs = new HashSet<String>(Arrays.asList("apple", "orange"));

        for (String str : strs) {
            str = "i" + str;
        }
    }
}

The issue with what you've written is you don't do anything with the computed value. The for-each loop sets the value of str to each element. Within the for loop you are changing the value of str, but not doing anything else with it.

This would be easy to do in a linkedlist or any data structure which supports indexing, but with a set it can be tricky. Just removing the old element and adding the new one will likely screw up the iteration, especially because you're dealing with a hash set.

A simple way to do this is to convert to a list and back:

class Test {
public static void main (String[] args) {
    Set<String> strs = new HashSet<String>(Arrays.asList("apple", "orange"));

    //Convert to list
    LinkedList<String> strsList = new LinkedList<String>();
    strsList.addAll(strs);

    //Do modification
    for (int i = 0; i < strsList.size(); i++) {
        String str = strsList.get(i);
        strsList.set(i,"i" + str);
    }

    //Convert back to set
    strs.clear();
    strs.addAll(strsList);
    }
}

This is clearly a bit more work than you would expect, but if mass-replacing is behavior you anticipate then probably don't use a set. I'm interested to see what other answers pop up as well.

You cannot modify a String in java, they are immutable. While it is theoretically possible to have mutable elements in a Set and mutate them in place, it is a terrible idea if the mutation effects hashcode (and equals).

So the answer to your specific question is no, you cannot mutate a String value in a Set without removing then adding entries to that Set .

The problem is that in the for loop, str is merely a reference to a String. References, when reassigned, don't change the actual object it refers to. Additionally, strings are immutable anyway, so calling any method on them will give you a new String instead of modifying the original. What you want to do is store all the new Strings somewhere, take out the old ones, and then add the new ones.

EDIT: My original code would have thrown a ConcurrentModificationException.

There are 3 problems in your approach to solve the problem.

1st - You can't modify the contents of a Set while you are iterating it. You would need to create a new Set with the new values.

"But I'm not modifying the set, I am modifying the objects within it", which leads to problem two

2nd - In your code you are modifying a reference to a string, not the string itself. To modify the string you would need to call a method over it, like string.changeTo("this") , or modify a field, string.value = "new value" .

Which leads to problem three.

3rd - Strings are immutable. when you construct a string, say new String("hello") , you can't further modify it's inner value.

The solutions :

First option is the simpler one, create a new set.

The second option is to use string builders instead of strings, which are mutable string creators/placeholders.

http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM