【问题标题】:Why do I need another new ArrayList instead of passing an existing one into ArrayList?为什么我需要另一个新的 ArrayList 而不是将现有的传递给 ArrayList?
【发布时间】:2015-01-11 21:44:31
【问题描述】:

我的问题是:

ArrayList<String> temp = new ArrayList<String>(r);

没有这一行,我什么都不会插入到res 为什么?

为什么不将r 复制到新的临时ArrayList 中,我没有将任何内容复制到res

public class Solution {

    public ArrayList<ArrayList<String>> partition(String s) {
        ArrayList<ArrayList<String>> res = new ArrayList<ArrayList<String>>();
        ArrayList<String> r = new ArrayList<String>();
        find(s, 0, r, res);
        return res;
    }

    private void find(String s, int st, ArrayList<String> r, ArrayList<ArrayList<String>> res) {
        if (st >= s.length()){
            // This seems important.  Without the next line, I am inserting
            // nothing into "res".  Why?
            ArrayList<String> temp = new ArrayList<String>(r);
            res.add(temp);
            return;
        } else {
            for (int i=st; i<s.length();i++) {
                if (isValid(s, st, i)) {
                    r.add(s.substring(st,i+1));
                    find(s, i + 1, r, res);
                    r.remove(r.size()-1);
                }
            }
        }
    }

    private boolean isValid(String s, int st, int end) {
        while (st < end) {
            if (s.charAt(st) != s.charAt(end)) {
                return false;
            }
            st++;
            end--;
        }
        return true;
    }
}

【问题讨论】:

  • 你试过res.add(r)吗?
  • 好吧,你以后修改r的(内容)
  • 那么,java是通过引用还是通过值传递arraylist?
  • 你试过res.add(r.clone());吗?
  • @BufBills Java 总是按值传递; 然而任何Object 的值都是它的参考地址。根据定义,ListObject

标签: java


【解决方案1】:

正如另一个答案所说,做:

res.add(r)

r 所引用的同一对象的引用添加到列表中。实际上,这里发生的是:

  • r 指的是一个列表
  • 该引用作为参数传递给add
  • 对列表的引用存储在res

但请注意,rres 内部的引用都指的是同一个对象

当您使用 temp 进行操作时,会发生以下情况:

  • 创建了一个新列表,其中包含r 引用的原始列表中所有数据的副本。
  • 此列表分配给temp
  • 对新列表的引用作为参数传递给add
  • 对列表的引用存储在res

所以现在r 指向列表的原始副本,res 内部的引用指向它的新副本。它们是两个不同的对象。

为什么这很重要?

基本上,您的递归步骤将一个元素添加到r,再次调用find,然后从r 中删除一个元素。 rres 中的引用传递给递归,因此这意味着这两个相同的对象传递给递归。

但是既然你递归返回后,你主动从r中移除了一个对象,也就意味着最后一路向上,r里面就没有更多元素了。

由于存储在res 中的引用指向同一个对象 r 指向而不是它的副本,这意味着当您使用r.remove() 删除项目时,该对象变为空。但这与我们从 res 内部引用的对象相同。所以在递归结束时,它将为空。

把它想象成一个保险箱。 A 人把钱装满了保险柜。然后他给 B 人第二把保险箱钥匙。然后他拿着原来的钥匙进去,把所有的钱都拿出来了。 B过来打开保险柜,里面是空的!

复制清单相当于 A 给 B 自己的钱放在自己的保险箱里。 A 可以从自己的保险箱中取出任意数量的钱,这不会改变 B 保险箱中的金额。

【讨论】:

    【解决方案2】:

    通过这样做:

     res.add(r);
    

    您将输入列表存储在列表的结果列表中。如果之后修改输入列表,这些更改也会出现在res 的条目中,因为它是同一个列表。

    ArrayList<String> temp = new ArrayList<String>(r);
    res.add(temp);
    

    然而,这会将输入列表r当前 内容复制到一个新列表中,并将这个新列表保存在结果列表res 中。

    不同之处在于,您不存储整个输入列表,而是存储其当前条目。因此,即使输入列表随后发生变化,它也不会影响保存在您的res 列表中的列表,因为res 将只包含输入列表的“快照”,在调用该方法时。

    传递数据有两个概念,分别称为“按值传递”和“按引用传递”。 Java 总是“按值传递”,但这并不意味着对象的所有字段都只是从一个地方复制到另一个地方。该对象的 地址 按值传递给新字段。阅读这篇文章以获得更详细的解释:https://stackoverflow.com/a/40523/2324078

    【讨论】:

      【解决方案3】:

      您需要此行,因为您在 find 之后删除了添加到 r 的项目:

      r.add(s.substring(st,i+1));
      find(s, i + 1, r, res);
      r.remove(r.size()-1); // <<<<<< HERE
      

      因此,当您仅将 r 添加到 res 而没有真正的副本时,您不仅会从 r 中删除行,还会从 res 中删除行 =)

      【讨论】:

        猜你喜欢
        • 2021-05-06
        • 2013-12-10
        • 2019-05-27
        • 1970-01-01
        • 2011-11-22
        • 2016-01-14
        • 2015-08-12
        • 1970-01-01
        • 2018-05-29
        相关资源
        最近更新 更多