在列表中引用变量

4

抱歉标题有点模糊,希望我在这里能让我的问题更清楚。

我有一个二维的List<List<Double>> myList,每个子列表包含两个项:一个明确的double,例如2.0,和一个double变量。基本上我想要做的是,不再将某些常量值存储在列表中,而是将其存储为对变量的引用,因为变量将在程序的其他部分更改,列表应该反映出这一点。这是我想要做的非常简化版本:

import java.util.Arrays;
import java.util.List;

public class Example {

    static double apples = 10.0;

    static double oranges = 5.0;

    static List<List<Double>> myList = Arrays.asList((Arrays.asList(2.0, apples)), (Arrays.asList(2.0, oranges)));

    public static void main(String[] args) {

        apples += 3;

        oranges += 3;

        for (List<Double> list : myList) {

            // list.get(1) -= list.get(0);

            System.out.println(list.get(1) - list.get(0));

        }

    }

}

程序的输出如下:
8.0
3.0

然而,应该是这样的:
11.0
6.0

由于 applesoranges 都增加了3。此外,请注意被注释的行 list.get(1) -= list.get(0);。我最终想要做的是将由 list.get(1) 指定的变量减去由 list.get(0) 指定的数量。然而,当我取消此行的注释时,会出现以下情况:

Error:(20, 25) java: unexpected type
  required: variable
  found:    value

我认为这两个问题都是由于“apple”和“orange”的值被存储在列表中作为常量值,而不是对变量本身的引用所导致的。这是因为我使用了“Arrays.asList()”来创建“myList”吗?还是因为我在定义“List >”时出现了问题?或者完全是其他问题?
谢谢您的任何帮助。

橘子和苹果的值被复制并自动装箱为Double实例。Double、Integer和String都是不可变的。你可以创建自己的包装类型,其中可以包含一个数字。 - Martín Zaragoza
你不能这样做。首先,你不能通过变量名访问列表的成员。你必须更新列表中的值。更新变量名的值将没有任何效果。然而,你可以通过使用元组来解决这个问题,并查找列表中的字符串名称,并更新与该字符串对应的值。从这段代码中我看到的最简单的解决方案是,在处理完值后,将值添加到列表中。 - Joel
所以,我从这些中得到的是,我不能在列表中有一个 Double 变量的“引用”。我可能会将其更改为,不是在列表中具有实际变量 applesoranges,而是只有像 "apples" 这样的字符串,并制作一个相应地更改变量的方法。 - Anish Shanbhag
它更新了列表中的值。列表和变量apples有两个不同的副本。您不能在同一语句中同时更新它们。 - Dawood ibn Kareem
[教我] 在这里你有一个误解。在Java中,没有办法存储、传递或表达对变量的引用。当然,你不能将“变量”存储在列表中。你只能存储一个值。最接近“列表中的引用”的是可变的DoubleDouble本身是不可变的)。 - user3458
显示剩余2条评论
2个回答

2

你遇到的主要问题与可变性和引用有关。

对象变量的值是引用,需要时进行取消引用。赋值运算符更改的是引用,而不是其下面的值。Java Double包装类是不可变的,因此不能更改它的值。

apples += 3;
oranges +=3;

这并不是把apples/oranges指向的对象的值增加3,而是创建一个新的Double对象,其值比apples/oranges大3。然后将apples/oranges分别赋值为对应对象的引用,您的列表仍然引用旧的对象。

您可以像Martín Zaragoza建议的那样创建一个包装类,它非常基础,只有一个公共成员和一个构造函数,如下所示:

public class MutableDouble {
    public double value
    public MutableDouble(double iV){
        this.value = iV
    }
}

您可以将苹果和橙子分配为MutableDouble值,并更改apples.value和oranges.value字段。

您还可以像Stephane K.在下面的评论中建议的那样使用AtomicReference。

include java.util.concurrent.atomic.AtomicReference;

然后:

AtomicReference apples = new AtomicReference(2.0);
System.out.println(apples);
apples.set((double)apples.get() + 5.0);
System.out.println(apples);

输出:

2.0
7.0

如果您改变底层对象,而不是将变量重新分配给新对象,则引用该对象的代码的其他区域将看到更改。

关于您遇到的减法问题,您正在尝试将一个值分配给另一个值。list.get(1)返回一个double,所以您基本上是在说 10.0 = 8.0。 请尝试这个:

list.set(1, list.get(1) - list.set(0));

这将会把减法的结果赋值给列表中索引为1的项。

你有没有任何想法,如果我将list.get(1)变成像“apples”这样的字符串,动态更新变量的方式?我正在尝试做类似于Example.class.getField(list.get(1)).get(Double) -= list.get(0);的操作,但是我得到了与原始代码相同的错误。我想象这不可能出现,因为上面提到的原因,但我仍然想尝试一下。 - Anish Shanbhag
我添加了一行关于使用list.get()和math时的错误提示,请检查一下。字符串(Strings)和浮点数(Doubles)一样是不可变的,你可以使用类似的可变字符串包装器类(MutableString wrapper class)。 - Travis
JDK中有已经实现了这些功能的类,不需要再新建一个:AtomicInteger、AtomicDouble... - stephane k.
它不起作用是因为get()返回一个值,你没有变量来分配你的新值。不要使用-=,而是使用set(index, get(index) - get(index))。请阅读我的评论中的最后一个代码块。 - Travis
Stephane K.,我看到了AtomicFloat,但没有AtomicDouble或AtomicString,也许可以使用AtomicReference<Double>和AtomicReference<String>? - Travis
显示剩余4条评论

1
例子以便理解。
public static void main( String[] args ) {
    class testClass {
        int b;

        @Override
        public String toString() {
            return "testClass{" +
                    "b=" + b +
                    '}';
        }
    }
    List<Double> d = new ArrayList<>();
    Double dd = new Double("2.0");
    d.add(dd);
    System.out.println(d.toString());
    dd = 3.0;
    System.out.println(d.toString());


    List<testClass> tList = new ArrayList<>();
    testClass t = new testClass();
    t.b =1;
    tList.add(t);
    System.out.println(tList.toString());
    t.b=2;
    System.out.println(tList.toString());
}

输出
[2.0]
[2.0]
[testClass{b=1}]
[testClass{b=2}]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接