【问题标题】:Does Java have mutable types for Integer, Float, Double, Long?Java 是否具有 Integer、Float、Double、Long 的可变类型?
【发布时间】:2011-05-30 00:58:27
【问题描述】:

我想使用 Integer 之类的可变版本。我必须使用这些类(如下)还是 Java 有内置的东西?

http://www.java2s.com/Code/Java/Data-Type/Amutableintwrapper.htm

【问题讨论】:

  • 问题是你为什么要这样做?
  • 对于某些情况(例如,游戏,存储一块带有n卡路里的食物,可以消耗/添加),使用以使用命名的类可能会更好, (例如class FoodItem { int calories; },因为它更清晰,如果以后需要可以添加方法。
  • Java 8 lambda 仅适用于有效的最终变量。为了解决这个限制,需要一个可变的 int。
  • 您可能需要一个需要在方法之间传递的计数器。传递 int 不起作用,就好像它在一种方法中递增然后该值不会反映在另一种方法中。

标签: java numbers integer mutable built-in


【解决方案1】:

如果包含可变包装类的代码太麻烦,您总是可以将值包装在像 int[] mutable = {1}; 这样的数组中。

【讨论】:

  • 聪明,丑陋的罪孽。
  • 这非常聪明,几乎不占用空间。 plus 避免了使用 AtomicInt 完成的同步。
【解决方案2】:

从 JDK 1.5 开始,java 现在有了 java.util.concurrent.atomic.AtomicInteger

这是一个线程安全的可变整数,使用示例:

final AtomicInteger value = new AtomicInteger(0);

然后:

value.incrementAndGet();

【讨论】:

  • 这种安全性是以性能为代价的,在许多情况下是不需要的。例如,我的一个常见用例是增加一个由 lambda 捕获的计数器。使用原子是多余的。
【解决方案3】:

不,Java 没有这些内置功能。这是有原因的。使用可变类型是危险的,因为它们很容易被滥用。此外,它很容易实现。例如,commons-lang 有一个MutableInt

【讨论】:

  • 我的猜测是 Java 开发人员希望 Integer 像 int 一样“表现”,也就是说……一旦你引用了它,它就永远不会改变(避免混淆)。但是..对我来说,以某种方式没有可变选项似乎仍然很奇怪......
  • “使用可变类型很危险,因为它们很容易被滥用。”大多数东西都可能被滥用。不可变类型存在于安全很重要的情况下,尽管它们可以通过反射来改变。解决了这些情况,没有理由阻止人们在需要时使用可变类型。
  • 一种更好的说法,不可变类型在例如地图和集合。如果你有一个可变的 Integer 并在它作为键的地方改变了它的值,它会弄乱集合。但是,如果您需要 Integer 的一些可变实现,没有什么比创建一些内部具有 int 值的类更容易的了。你可以在那里发挥创造力 - 让它成为例如一个 Counter 或 Countdown,而不仅仅是一个可以实现任何功能的普通 int。给它一些逻辑(除非您使用自下而上的 Java EE 进行开发)。
  • 当然是一个老问题,但希望有人能回答他们是如何被滥用的。使用它们有什么危险?
  • @user1175807 正如 Vlasec 所评论的,如果您不小心修改了在其他地方使用过的变量,则会导致奇怪的错误。更糟糕的是,假设您正在编写一个与客户代码一起工作的程序。您需要实现一个具有可变整数类型参数的方法。现在如果你出于某种原因修改了可变Integer,那么它也会在客户端的程序中被改变,这是完全出乎意料的,并且会导致难以发现的错误。
【解决方案4】:

这是我为可变整数制作的一个小类:

public class MutableInteger {
    private int value;
    public MutableInteger(int value) {
        this.value = value;
    }
    public void set(int value) {
        this.value = value;
    }
    public int intValue() {
        return value;
    }
}

您可以轻松地将其扩展到任何其他原语。当然,就像其他人所说的那样,您应该谨慎使用它。

【讨论】:

  • 恕我直言,最好的选择是它不会像使用原子整数那样提供有关并发性的错误线索。还有一个数组,真的,我永远不会在我的代码中做这样的坏事.. ;)
  • 提供单个通用包装类可能是个好主意,这样您就不会得到MutableIntegerMutableDoubleMutableString 等,而是有一个Mutable<Integer> , Mutable<Double>, ... 内存开销(使用 Integer 而不是 int 通常不会考虑在内。但是你会得到一个可以处理大多数情况的单一的、随时可用的类(如果你希望你的整数是可比较或相似的东西,但您仍然需要子类化)。
  • 扩展Number可能是个好主意。
【解决方案5】:

您可以将 nnnn[] 用作@Alexandre 建议的任何原始类型的可变对象,java 也有 AtomicInteger 和 AtomicLong。

恕我直言,int 通常是比 Integer 更好的选择,而且它是可变的。

您能否详细说明为什么需要多个对象,也许还有另一种方法可以实现相同的目标。

【讨论】:

  • int 总是可变的,除非它也是 final
  • 说原始类型是可变是不正确的。它可以更改,但如果您只是指向一个新对象,那么每个非最终对象也可以。例如Integer a = 4; 那么a = 5; 是有效代码,但Integer 是不可变的。
  • 我同意 Integer 实例是不可变的,即使对它们的引用是可变的,但这是不同的类型。
  • 这不是有一个引用 int 参数的好方法吗?例如,一个函数返回一个分页的项目列表(来自数据库),但也返回记录总数。在这种情况下,AtomicInteger 或 MutableInteger 似乎很有用。当然,另一种方法是拥有一个 getTotalRecords 属性,而不是在同一个方法中返回它。
  • int 是不可变的,因为如果将其传递给方法,则方法无法更改其值并使新值反映在调用方法中
【解决方案6】:

AtomicInteger 已经被提及。可变的Doubles 可以用AtomicReference<Double> 模拟。已经提到的警告适用,它的风格很糟糕,但有时你有这样的代码

double sum=0
for (Data data:someListGenerator())
  sum+=data.getValue()

并希望以功能性 Java 8 风格重构它。如果代码遵循这种模式但增加了相当大的复杂性,那么最明智的转换可能

AtomicReference<Double> sumref=new AtomicReference<>(0d);
someStreamGenerator().forEach(data->
  sumref.set(sumref.get().doubleValue()+data.getValue()));
double sum=sumref.get().doubleValue();

当然,这至少是有问题的风格。但我不止一次地发现自己在ResultSet 计算上出现了一个扭曲循环,并从中部分累积了三个不同的信息。这使得将代码转换为正确的函数样式变得非常困难。根据上述模式转换累积部分在我看来是干净代码和过度简化重构之间的合理权衡。

【讨论】:

  • 您应该认真考虑一下您的措辞:即使您使用 lambda,您的示例也不是功能性,因为它禁止了副作用。如果你真的把它写成函数式的,你就不需要变量:someStreamGenerator().mapToDouble(Data::getValue).sum()。即使是累积三个不同的信息,也有一个功能的方式,使用Stream.reduceStream.collect。我认为没有理由将每个循环都重构为功能代码片段,但如果你想这样做,你应该坚持到最后。
  • 正如我所写:这是正确的样式,对于每个和任何新代码都应该使用。但是,当重构 400.000 多行代码并在 15 年多的时间里演变时,您会发现将构造正确地重写为真正的功能构造可能极其困难。然后,重写为这种混合风格可能是一个明智的权衡,因为您至少可以使基本结构保持一致。 forforeach 可以是在功能上重写的复杂框架的一部分,因此您必须以某种方式围绕它们对齐其余代码。
  • 你混淆了 lambdas 和函数式编程。在您的示例中,您没有将其重写为“功能性”。当您失去可读性但没有获得函数式编程的优势时,用 lambda 重写所有内容有什么意义?为什么要尝试通过使用这种混合来摆脱复杂框架中的每个循环?
  • 您的代码中有 1000 个具有特定习语的地方。该成语由库或任何全局结构驱动。您用基于函数式方法和 lambda 的新结构替换该全局结构。 1000 个地方中有 998 个可以干净利落地转换为实用风格。剩下的 2 个很难正确转换。在这种情况下,我总是要求用这种混合结构转换成新的风格。只有这样,您才能摆脱代码中的旧全局结构。虽然并不完美,但整体代码质量有所提高。
【解决方案7】:

您可以导入 org.omg.CORBA 包(或只导入您需要的类)并在其中使用 Holder 类。

例如,它具有“IntHolder”,其中存储整数的字段是公共的,可以修改它。

public static void triple(IntHolder x){
    x.value = 3 * x.value;
}

IntHolder mutableInt = new IntHolder(10);
triple(mutableInt);     
System.out.println(mutableInt.value);

它还有“LongHolder”和“DoubleHolder”以及许多其他您可以使用的东西。谨慎使用。

这里是它的 api:https://docs.oracle.com/javase/7/docs/api/org/omg/CORBA/package-summary.html

【讨论】:

  • 虽然解决问题的方法非常聪明,但随着 Java9 的出现,您可能不想使用它——它会导入整个 CORBA 模块,只是为了一个 int 持有者。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-25
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 2022-11-25
  • 2013-04-25
相关资源
最近更新 更多