ChatGPT解决这个技术问题 Extra ChatGPT

在 Java 中安全地将 long 转换为 int

在 Java 中验证从 longint 的转换不会丢失任何信息的最惯用方法是什么?

这是我目前的实现:

public static int safeLongToInt(long l) {
    int i = (int)l;
    if ((long)i != l) {
        throw new IllegalArgumentException(l + " cannot be cast to int without changing its value.");
    }
    return i;
}
两条代码路径。一个是遗留和需要整数。遗留数据应该都适合 int,但如果违反该假设,我想抛出异常。另一个代码路径将使用 long 并且不需要强制转换。
我喜欢人们总是质疑你为什么要做你想做的事。如果每个人都在这些问题中解释了他们的全部用例,那么没有人能够阅读它们,更不用说回答它们了。
BT - 因为这个原因,我真的很讨厌在网上问任何问题。如果您想提供帮助,那很好,但不要玩 20 个问题并强迫他们为自己辩护。
在这里不同意 BT 和 Mason240:向提问者展示他们没有想到的另一个解决方案通常很有价值。标记代码异味是一项有用的服务。从“我很好奇为什么……”到“强迫他们为自己辩护”还有很长的路要走。
有很多事情你不能用 long 做,例如索引一个数组。

N
Neuron

Java 8 添加了一个新方法来做到这一点。

import static java.lang.Math.toIntExact;

long foo = 10L;
int bar = toIntExact(foo);

如果溢出,将抛出 ArithmeticException

请参阅:Math.toIntExact(long)

Java 8 中添加了其他几种溢出安全方法。它们以exact 结尾。

例子:

Math.incrementExact(long)

Math.subtractExact(long, long)

Math.decrementExact(long)

Math.negateExact(long),

Math.subtractExact(int, int)


我们还有 addExactmultiplyExact。值得注意的是除法 (MIN_VALUE/-1) 和绝对值 (abs(MIN_VALUE)) 没有安全的便捷方法。
但是使用 Math.toIntExact() 而不是通常转换为 int 的区别是什么? Math.toIntExact() 的实现只是将 long 转换为 int
@YamashiroRion 实际上, toIntExact 的实现首先检查强制转换是否会导致溢出,在这种情况下它会引发 ArithmeticException。只有当强制转换是安全的时,它才会执行从 long 到返回的 int 的强制转换。换句话说,如果您尝试转换一个无法表示为 int 的长数字(例如,任何严格高于 2 147 483 647 的数字),它将引发 ArithmeticException。如果你用一个简单的转换做同样的事情,你得到的 int 值将是错误的。
J
James McMahon

我想我会这样做:

public static int safeLongToInt(long l) {
    if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
        throw new IllegalArgumentException
            (l + " cannot be cast to int without changing its value.");
    }
    return (int) l;
}

我认为这比重复演员更清楚地表达了意图……但这有点主观。

注意潜在的兴趣 - 在 C# 中它只是:

return checked ((int) l);

我总是将范围检查作为 (!(Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE))。我发现很难理解其他方法。可惜 Java 没有 unless
+1。这完全符合“例外情况应用于例外情况”规则。
(在现代通用语言中,它会是:“嗯?但是整数有任意大小?”)
@Tom:我猜是个人喜好 - 我更喜欢尽可能少的负面因素。如果我正在查看带有引发异常的主体的“if”,我希望看到使其看起来异常的条件 - 例如值“超出 int 的底端”。
@Tom:在那种情况下,我会删除否定,将强制转换/返回放在“if”主体中,然后抛出异常,如果你明白我的意思的话。
H
Henrik Aasted Sørensen

使用 Google Guava 的 Ints 类,您的方法可以更改为:

public static int safeLongToInt(long l) {
    return Ints.checkedCast(l);
}

从链接的文档:

checkedCast public static int checkedCast(long value) 如果可能,返回等于 value 的 int 值。参数: value - int 类型范围内的任何值 返回:等于 value 的 int 值 抛出:IllegalArgumentException - 如果 value 大于 Integer.MAX_VALUE 或小于 Integer.MIN_VALUE

顺便说一句,您不需要 safeLongToInt 包装器,除非您想保留它以更改功能而无需进行大量重构。


顺便说一句,Guava 的 Ints.checkedCast 与 OP 完全一样
为 Guava 解决方案 +1,虽然不需要将其包装在另一种方法中,只需直接调用 Ints.checkedCast(l) 即可。
Guava 也有 Ints.saturatedCast 它将返回最接近的值而不是抛出异常。
是的,在我的情况下,使用现有的 api 是安全的,项目中已经存在库:如果无效则抛出异常: Ints.checkedCast(long) 和 Ints.aturatedCast(long) 以获得最近的将 long 转换为 int。
J
Jaime Saiz

使用 BigDecimal:

long aLong = ...;
int anInt = new BigDecimal(aLong).intValueExact(); // throws ArithmeticException
                                                   // if outside bounds

我喜欢这个,有人反对这个解决方案吗?
好吧,它分配和丢弃 BigDecimal 只是为了获得应该是一种实用方法,所以是的,这不是最好的过程。
@Riking 在这方面,最好使用 BigDecimal.valueOf(aLong) 而不是 new BigDecimal(aLong) 来表示不需要新实例。执行环境是否对该方法进行缓存,是特定于实现的,就像可能存在的逃逸分析一样。在大多数现实生活中,这对性能没有影响。
E
Eric Woodward

这是一个解决方案,以防您不关心价值,以防它比需要的更大;)

public static int safeLongToInt(long l) {
    return (int) Math.max(Math.min(Integer.MAX_VALUE, l), Integer.MIN_VALUE);
}

看来,你错了......它会很好然后消极。另外,too low 是什么意思?请提供用例。
此解决方案是快速且安全的解决方案,然后我们正在讨论将 Long 转换为 Int 以服从结果。
A
Andreas

不要:这不是解决方案!

我的第一种方法是:

public int longToInt(long theLongOne) {
  return Long.valueOf(theLongOne).intValue();
}

但这只是将 long 转换为 int,可能会创建新的 Long 实例或从 Long 池中检索它们。

缺点

Long.valueOf 如果数字不在 Long 的池范围 [-128, 127] 内,则创建一个新的 Long 实例。 intValue 实现无非就是: return (int)value;

因此,这可以被认为比仅将 long 转换为 int 更糟糕。


感谢您提供帮助的尝试,但举一个不起作用的例子与提供一个有效的解决方案并不完全相同。如果您要编辑以添加正确的方法,那可能会很好;否则,它并不适合作为答案发布。
好的,为什么不同时拥有 DO 和 DONT? Tbh,有时我希望我有一个如何不做事(DONTs)的列表来检查我是否使用了这样的模式/代码。无论如何,我可以删除这个“答案”。
良好的反模式。无论如何,如果您解释了如果 long 值超出 int 范围会发生什么,那就太好了?我想会有一个 ClassCastException 或类似的东西?
@PeterWippermann:我添加了更多信息。你认为他们可以理解吗?足够解释吗?
T
Tom Hawtin - tackline

我声称查看转换值是否改变值的明显方法是转换并检查结果。但是,我会在比较时删除不必要的演员表。我也不太喜欢单字母变量名(例外 xy,但不是当它们表示行和列时(有时分别))。

public static int intValue(long value) {
    int valueInt = (int)value;
    if (valueInt != value) {
        throw new IllegalArgumentException(
            "The long value "+value+" is not within range of the int type"
        );
    }
    return valueInt;
}

但是,如果可能的话,我真的想避免这种转换。显然有时这是不可能的,但在那些情况下,几乎可以肯定 IllegalArgumentException 是就客户端代码而言抛出的错误异常。


这就是 Google Guava Ints::checkedCast 的最新版本所做的。
m
mob

Java 整数类型表示为有符号。如果输入介于 231 和 232(或 -231 和 -232)之间,则强制转换会成功,但您的测试会失败。

要检查的是 long 的所有高位是否都相同:

public static final long LONG_HIGH_BITS = 0xFFFFFFFF80000000L;
public static int safeLongToInt(long l) {
    if ((l & LONG_HIGH_BITS) == 0 || (l & LONG_HIGH_BITS) == LONG_HIGH_BITS) {
        return (int) l;
    } else {
        throw new IllegalArgumentException("...");
    }
}

我不明白签名与它有什么关系。你能举一个不会丢失信息但没有通过测试的例子吗? 2^31 将被转换为 Integer.MIN_VALUE(即 -2^31),因此信息已丢失。
@Jon Skeet:也许我和 OP 正在互相交谈。 (int) 0xFFFFFFFF(long) 0xFFFFFFFFL 具有不同的值,但它们都包含相同的“信息”,从 int 中提取原始 long 值几乎是微不足道的。
当 long 可以从 -1 开始而不是 0xFFFFFFFF 时,如何从 int 中提取原始 long 值?
对不起,如果我不清楚。我是说如果 long 和 int 都包含相同的 32 位信息,并且如果设置了第 32 位,则 int 值与 long 值不同,但它很容易得到long值。
@mob 这是指什么? OP 的代码正确报告长值 > 2^{31} 不能转换为整数
M
Maury
(int) (longType + 0)

但 Long 不能超过最大值:)


+ 0 不会向此转换添加任何内容,如果 Java 以与字符串类似的方式处理数字类型连接,它可能会起作用,但因为它不会无缘无故地执行添加操作。
R
Rajat Anantharam

另一种解决方案可以是:

public int longToInt(Long longVariable)
{
    try { 
            return Integer.valueOf(longVariable.toString()); 
        } catch(IllegalArgumentException e) { 
               Log.e(e.printstackstrace()); 
        }
}

对于客户端正在执行 POST 并且服务器 DB 仅了解整数而客户端具有 Long 的情况,我已经尝试过此方法。


您将在“real long”值上获得 NumberFormatException:Integer.valueOf(Long.MAX_VALUE.toString()); 导致 java.lang.NumberFormatException: For input string: "9223372036854775807" 几乎混淆了 Out-Of-Range-Exception,因为现在它的处理方式与处理包含字母的字符串相同。
它也不会编译,因为您并不总是返回一个值。