【问题标题】:Modifying elements while iterating Java Set在迭代 Java Set 时修改元素
【发布时间】:2014-08-06 23:57:29
【问题描述】:

这里是 Java 新手。我来自 Python。在迭代 Java Set 时,关于删除或添加元素的 SO 也有类似的问题。我想知道的是修改集合中包含的元素。例如,["apple", "orange"]["iapple", "iorange"]。另外,我想在原地做,即不创建另一个集合,而是在迭代时将修改后的元素放入新集合中。

显然一个简单的for循环是行不通的,如下:

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;
        }
    }
}

【问题讨论】:

  • 函数式方法是通过对原始集合应用转换函数来创建新集合。新集合可能只是对原始集合和转换函数的引用——技术到位。
  • @emory Yap。我想到了FP方法。我的印象是Java并不是特别适合FP。您介意提供代码示例吗?
  • 我认为您正在寻找类似stackoverflow.com/a/19710021/348975

标签: java


【解决方案1】:

你所写的问题是你没有对计算值做任何事情。 for-each 循环将 str 的值设置为每个元素。在 for 循环中,您正在更改 str 的值,但不对其进行任何其他操作。

这在链表或任何支持索引的数据结构中很容易做到,但使用集合可能会很棘手。仅删除旧元素并添加新元素可能会搞砸迭代,尤其是因为您正在处理哈希集。

执行此操作的一种简单方法是转换为列表并返回:

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);
    }
}

这显然比您预期的要多一些工作,但如果大规模替换是您预期的行为,那么可能不要使用集合。 我很想看看还有什么其他答案。

【讨论】:

    【解决方案2】:

    您不能在 java 中修改 String,它们是不可变的。 虽然理论上可以在 Set 中包含可变元素并在适当位置对其进行变异,但如果变异影响哈希码(和等于),则它是 terrible idea

    因此,您的具体问题的答案是否定的,您不能在不删除然后向该 Set 添加条目的情况下更改 Set 中的 String 值。

    【讨论】:

      【解决方案3】:

      问题在于,在 for 循环中,str 只是对字符串的引用。重新分配引用时,不会更改它所引用的实际对象。此外,字符串无论如何都是不可变的,因此在它们上调用任何方法都会为您提供一个新字符串,而不是修改原始字符串。您要做的是将所有新字符串存储在某个地方,取出旧字符串,然后添加新字符串。

      编辑:我的原始代码会引发 ConcurrentModificationException。

      【讨论】:

      • 我认为这会引发 ConcurrentModificationException,因为当您从它正在迭代的集合中添加/删除项目时,java 不喜欢它。
      • 是的。我知道为什么 for-loop 不起作用,这就是为什么我说“显然它不起作用”。 :) 只是好奇,为什么它被否决了?编辑:明白了。因为 ConcurrentModificationException。
      • str is merely a reference to a String - 这是什么意思?如果您使用了其他对象,那么 obj is a reference to a ObjectString 不可变无关。
      • 对不起,我不清楚。我真的只是想指出重新分配引用只是这样做 - 它改变了引用而不是引用引用的对象。例如,整数 a = new Integer(5);整数 b = a;整数 a = 新整数(4);现在 a = Integer(4) 但 b 仍然等于 Integer(3)。我提到字符串是不可变的,这是另一个单独的原因,如果不调用集合上的操作,for 循环就无法工作。
      【解决方案4】:

      您解决问题的方法中有 3 个问题。

      1st - 迭代时不能修改 Set 的内容。您需要使用新值创建一个新 Set。

      “但我不是在修改集合,我是在修改其中的对象”,这就导致了问题二

      2nd - 在您的代码中,您正在修改对字符串的引用,而不是字符串本身。 要修改字符串,您需要对其调用方法,如string.changeTo("this"),或修改字段,string.value = "new value"

      这就引出了问题三。

      3rd - 字符串是不可变的。当你构造一个字符串时,比如new String("hello"),你不能进一步修改它的内部值。

      解决方案

      第一个选项比较简单,创建一个新集合。

      第二个选项是使用字符串构建器而不是字符串,它是可变的字符串创建器/占位符。

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

      【讨论】:

      • Set 中改变StringBuilder 实例可能会导致不可预知的结果。示例是 contains 返回不正确的数据、集合中存在重复项、remove 不工作等。
      猜你喜欢
      • 2014-02-23
      • 2020-09-07
      • 1970-01-01
      • 2011-02-21
      • 1970-01-01
      • 2011-11-14
      • 1970-01-01
      • 2014-01-04
      相关资源
      最近更新 更多