【问题标题】:For-Each and Pointers in Java [duplicate]Java中的For-Each和指针[重复]
【发布时间】:2010-12-21 20:11:45
【问题描述】:

好的,所以我很想遍历一个 ArrayList 并删除一个特定的元素。但是,我在使用类似 For-Each 的结构时遇到了一些麻烦。当我运行以下代码时:

ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)

for(String t : arr)
{
  t = " some other value "; //hoping this would change the actual array
}

for(String t : arr)
{
  System.out.println(t); //however, I still get the same array here
}

我的问题是,如何使 't' 成为指向 'arr' 的指针,以便能够更改 for-each 循环中的值?我知道我可以使用不同的结构循环遍历 ArrayList,但是这个结构看起来非常干净易读,如果能够将 't' 设为指针就好了。

感谢所有 cmets!即使你说我应该接受它并使用不同的构造。

【问题讨论】:

  • 这与 foreach 循环无关,这是对 java 工作原理的更基本的误解
  • 即使 ArrayList 由 Array 支持:ArrayList != Array.不要混淆术语(例如在您的 cmets 中)

标签: java pointers foreach arraylist


【解决方案1】:

我认为最好的方法可能是使用 for 循环。

    ArrayList<String> arr = new ArrayList<String>();

    for (int i = 0; i < arr.size(); i++) {

        String t = arr.get(i);

        if (// your condition is met) {
            arr.set(i, "your new value");
        }
    }

【讨论】:

  • 有效吗?这种方式没有 ConcurrentModificationException?
  • 我认为只有在迭代时从数组中添加/删除项目时才会出现该异常
  • 是的,当然,您不再使用迭代器,只是一个普通的 for 循环。没注意;)
  • 我想最好把 arr.size() 放在一个局部变量中,而不是每次都一遍又一遍地计算。
  • JIT 编译器很可能会将 size() 调用提升到循环之外。
【解决方案2】:

问题是你试图改变循环范围的reference t 让它指向一个新的 String 实例。这行不通。它不引用数组列表中的实际条目。您需要更改参考的实际。如果String 是可变的并为此提供了一个虚构的set() 方法,那么理论上你可以这样做

for (String t : arr) {
    t.set("some other value");
}

左右,但这是不可能的,因为它是不可变的。使用普通的for 循环更好地获取数组本身的入口点句柄:

for (int i = 0; i < arr.size(); i++) {
    arr.set(i, "some other value");
}

如果你坚持使用增强的for循环,那么你需要将String替换为StringBuilder,它是可变的:

for (StringBuilder t : arr) {
     t.delete(0, t.length()).append("some other value");
}

请记住,Java 是按值传递,而不是按引用传递。

【讨论】:

  • 更糟糕的是 - 他的第一句话告诉我们他想删除一些东西(有道理,似乎是 example.buab-user-group 的实际分配;))但他的代码显示了替换...这将是一个漫长的夜晚:)
  • Wtf,他想删除它。在那里你需要神圣的迭代器。
【解决方案3】:

for-each 没有给你一个索引指针,所以你不能用它来改变一个不可变的值。

要么使用带索引的 for 循环,要么使用可变类型(如 StringBuffer,而不是 String)

【讨论】:

    【解决方案4】:

    Java 中的对象数组(如字符串)是包含一系列有序引用的连续块。所以,当你有一个由 4 个字符串组成的数组时,你真正拥有的是存储在数组中的 4 个引用,以及 4 个在数组之外但被其 4 个元素引用的字符串对象。

    Java 中的 for-each 结构所做的是创建一个局部变量,并且对于每次迭代,将对应于该迭代的数组单元格的引用复制到该局部变量中。当您设置循环变量 (t = " some other value") 时,您会将对新字符串 "some other value" 的引用放入局部变量 t,而不是放入数组中。

    与其他一些语言(如 Perl)的对比,其中循环变量就像数组/列表元素本身的别名。

    【讨论】:

    • +1 以获得惊人的解释!
    【解决方案5】:

    您的代码被编译器重写为:

    ArrayList<String> arr = new ArrayList<String>();
    //... fill with some values (doesn't really matter)
    
    for (final Iterator <String> i = arr.iterator(); i.hasNext();) {
        String t;
    
        t = i.next();
        t = " some other value "; // just changes where t is pointing
    }
    

    要执行您想要的操作,您必须像这样编写 for 循环:

    for (final ListIterator<String> i = arr.iterator(); i.hasNext();) {
         final String t;
    
         t = i.next();
         i.set("some other value");
    }
    

    Iterator 没有 set 方法,只有 ListIterator 有。

    【讨论】:

      【解决方案6】:

      基本上你想从列表 arr 中删除字符串 t。只需执行 arr.remove(t) 即可。 但是你不能在迭代同一个列表时这样做。如果您尝试以这种方式修改列表,则会收到异常。

      你有两个选择:

      1. 克隆您的列表,遍历克隆并从原始列表中删除“特定”字符串
      2. 为删除候选者创建一个列表,将所有“特定”字符串添加到该列表中,并在遍历原始列表后,遍历废纸篓并从原始列表中删除您在此处收集的所有内容。

      选项1是最简单的,克隆可以像这样:

      List<String> clone = new ArrayList<String>(arr);
      

      【讨论】:

        【解决方案7】:

        您似乎误解了对象/引用在 Java 中的工作方式,这是有效使用该语言的基础。但是,这里的代码应该做你想做的(抱歉没有解释):

        ArrayList<String> arr = new ArrayList<String>();
        //... fill with some values (doesn't really matter)
        
        for(int i = 0; i < arr.size(); i++)
        {
          arr.set(i, " some other value "); // change the contents of the array
        }
        
        for(String t : arr)
        {
          System.out.println(t); 
        }
        

        【讨论】:

          【解决方案8】:

          我相信,这与不可变或可变无关。

           t = " some other value "; //hoping this would change the actual array
          

          t 不包含对实际对象的引用。 Java 复制 arraylist 中的值并将该值放入 t 中,因此数组列表值不会受到影响。

          HTH

          【讨论】:

          • 这实际上更符合我最初的想法。我想知道我是否可以获得对该对象的引用,但由于它是不可变的,我想这并不重要。
          【解决方案9】:

          这已得到很好的回答。仍然是我的建议。 var t inside 循环仅在此处可见。它不会在循环之外看到。如果不是String,您可以使用t.set()

          【讨论】:

            【解决方案10】:

            使用StringBuffer 而不是纯字符串。这样里面的字符串是可变的。

            【讨论】:

              【解决方案11】:

              字符串是不可变的。如果您有像 StringBuilder/Buffer 这样的可变类型,则可以在迭代中更改字符串。你确实有参考,记住。

              【讨论】:

              • 这与StringStringBuilder或不变性无关,这是关于对象引用在Java中的工作方式。
              • 代码赋值给局部变量t,这不会影响ArrayList内部的引用。要更改这一点,您需要根据 MB 的以下回答调用 arr.set()。
              • ...什么?根据 OP 定义的规范,遍历 ArrayList 肯定会让您更改内联对象。他说他想要风格,而不是精确的指针功能。你们都是傀儡。
              • 人们,不变性点是因为 OP 评论“//希望这会改变实际的数组”。所以这个答案并不比投票率高的 MB 用新对象替换数组元素位置中的对象的答案差。
              • @Tofu:你读过OP的问题吗?他询问更改 VALUE,而不是指针引用。在很多情况下,将变量放在列表中,然后对其进行整体修改。这不是“潜在危险”。它是“按设计运行”。
              猜你喜欢
              • 2015-10-02
              • 1970-01-01
              • 2023-03-18
              • 1970-01-01
              • 1970-01-01
              • 2011-07-21
              • 2017-01-02
              • 2014-03-03
              • 2010-12-03
              相关资源
              最近更新 更多