用Java安全地将long转换为int


488

Java中最惯用的方法是验证 long到的转换int不会丢失任何信息?

这是我当前的实现:

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;
}

34
两个代码路径。一种是遗产,需要诚信。该旧数据应该都适合int,但是如果违反该假设,我想抛出一个异常。另一个代码路径将使用long,并且不需要强制转换。
百翰姆

197
我喜欢人们总是质疑您为什么想要做自己想做的事情。如果每个人都在这些问题中解释了他们的完整用例,那么没有人能够阅读它们,更不用说回答了。
英国电信

24
BT-出于这个原因,我真的很讨厌在线问任何问题。如果您想提供帮助,那很好,但是不要玩20个问题,不要强迫他们自圆其说。
Mason240

59
在这里与BT和Mason240意见不一致:向提问者展示他们没有想到的另一种解决方案通常很有价值。标记代码气味是一项有用的服务。从“我对为什么...很好奇”到“迫使他们为自己辩护”还有很长的路要走。
汤米·赫伯特

13
使用long不能做很多事情,例如索引数组。
skot

Answers:


579

Java 8已添加了一个新方法来完成此任务。

import static java.lang.Math.toIntExact;

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

会抛出一个 ArithmeticException溢出的情况。

看到: Math.toIntExact(long)

Java 8中已经添加了其他几种溢出安全方法,它们的结尾是精确

例子:

  • Math.incrementExact(long)
  • Math.subtractExact(long, long)
  • Math.decrementExact(long)
  • Math.negateExact(long),
  • Math.subtractExact(int, int)

5
我们也有addExactmultiplyExact。值得注意的是,除法(MIN_VALUE/-1)和绝对值(abs(MIN_VALUE))没有安全的便捷方法。
Aleksandr Dubinsky

但是使用Math.toIntExact()而不是通常的强制转换有int什么区别?Math.toIntExact()just 的实现强制转换longint
Yamashiro Rion

@YamashiroRion实际上,toIntExact的实现首先会检查强制类型转换是否会导致溢出,在这种情况下,它将引发ArithmeticException。仅在强制转换安全的情况下,才执行从long到int的强制转换,然后返回。换句话说,如果您尝试强制转换不能表示为int的长整数(例如,严格高于2 147 483 647的任何数字),它将抛出ArithmeticException。如果使用简单的转换执行相同的操作,则结果int值将是错误的。
Pierre-Antoine

306

我想我会简单地做到这一点:

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);

7
我总是将范围检查设为(!(Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE))。我发现很难以其他方式做到这一点。可惜Java没有unless
Tom Hawtin-大头钉

5
+1。这完全属于“例外情况下应使用例外情况”规则。
亚当·罗森菲尔德2009年

4
(用现代通用语言是:“嗯?但是整数有任意大小吗?”)
汤姆·霍汀-钉路

7
@汤姆:我想个人喜好-我宁愿尽量减少负面因素。如果我正在查看带有引发异常的主体的“ if”,我想查看使它看起来异常的条件-例如该值位于的“底端” int
乔恩·斯基特

6
@Tom:在这种情况下,我将删除负片,将强制转换/返回放置在“ if”主体内,然后,如果您明白我的意思,则在之后引发异常。
乔恩·斯基特

132

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

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

从链接的文档中:

CheckedCast

public static int checkedCast(long value)

value如果可能,返回等于的int值。

参数: value - int类型范围内的任何值

返回:int等于 的值value

抛出: IllegalArgumentException -如果value大于Integer.MAX_VALUE或小于Integer.MIN_VALUE

顺便说一句,您不需要safeLongToInt包装器,除非您希望将其保留在适当的位置以用于更改功能而无需进行大量重构。


3
番石榴的Ints.checkedCast作用恰恰是OP的作用,顺便说一下
部分多云

14
+1用于番石榴解决方案,尽管实际上不需要将其包装在另一个方法中,只需Ints.checkedCast(l)直接调用即可。
dimo414

8
番石榴还具有Ints.saturatedCast将返回最接近的值,而不是引发异常的方法。
杰克·沃尔什

是的,使用现有的api(在项目中已经存在的库)作为我的实例是安全的:如果无效,则引发异常:Ints.checkedCast(long)和Ints.saturatedCast(long)获取最接近的整数,以便将long转换为int。
Osify

29

使用BigDecimal:

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

我喜欢这个,有人反对这个解决方案吗?
Rui Marques 2013年

12
好吧,它只是分配并丢弃一个BigDecimal来获取应该是一种实用程序的方法,所以,那不是最好的过程。
2014年

在这方面,@ Riking最好使用BigDecimal.valueOf(aLong)而不是new BigDecimal(aLong)来表示不需要新实例。执行环境是否按该方法进行缓存是特定于实现的,就像可能存在Escape Analysis一样。在大多数现实生活中,这对性能没有影响。
Holger

17

这是一个解决方案,以防您不在乎价值,而不是价值;

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

看来,您错了...效果会很好,然后是负面的。另外,这是什么意思too low?请提供用例。
Vitaliy Kulikov

此解决方案是快速且安全的解决方案,那么我们正在谈论将Long转换为Int以遵守结果。
Vitaliy Kulikov

11

不要:这不是解决方案!

我的第一种方法是:

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

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


缺点

  1. Long.valueOfLong如果该数字不在Long的池范围[-128,127]中,则创建一个新实例。

  2. intValue实现无非是:

    return (int)value;

因此,这可能比将longto 强制转换为更糟糕int


4
感谢您尝试提供帮助,但是举一个无法解决问题的例子与提供可以解决问题的解决方案并不完全相同。如果您要编辑以添加正确的方法,这可能会很好。否则,不适合作为答案发布。
2013年

4
好吧,为什么不同时拥有DO和DONT?TBH,有时候我希望我有一份关于如何不做事(DONT)的清单,以检查我是否使用了这种模式/代码。无论如何,我可以删除此“答案”。
Andreas 2013年

1
好的反模式。无论如何,如果您解释了如果long值超出int范围会发生什么,那将是很棒的?我猜会有ClassCastException或类似的东西吗?
彼得·威珀曼

2
@PeterWippermann:我添加了更多信息。您认为他们可以理解吗?解释够了吗?
Andreas

7

我声称,查看强制转换值是否更改值的明显方法是强制转换并检查结果。但是,在比较时,我会删除不必要的强制类型转换。我也不是很热衷于一个字母变量名(exception 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,几乎可以肯定的是,就客户端代码而言,是抛出了错误的异常。


1
这就是Google Guava Ints :: checkedCast的最新版本。
lexicalscope 2014年

2

Java整数类型表示为带符号。输入介于2 31和2 32之间(或-2 31和-2 32之间)),则转换会成功,但测试会失败。

要检查的是的所有高位是否都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("...");
    }
}

3
我看不出签名与它有什么关系。你能给它一个例子丢失信息,但不会失败的考验吗?2 ^ 31将被强制转换为Integer.MIN_VALUE(即-2 ^ 31),因此信息已丢失。
乔恩·斯基特

@Jon Skeet:也许我和OP正在互相交谈。(int) 0xFFFFFFFF(long) 0xFFFFFFFFL具有不同的值,但是它们都包含相同的“信息”,并且从int提取原始long值几乎是微不足道的。
暴徒

当long开始时可能是-1而不是0xFFFFFFFF时,如何从int提取原始long值?
乔恩·斯基特

很抱歉,如果我不清楚。我的意思是,如果long和int都包含相同的32位信息,并且如果设置了32nd位,则int值与long值不同,但是很容易获得长期价值
暴民

@mob指的是什么?OP的代码正确报告长值> 2 ^ {31}无法转换为整数
局部多云

0
(int) (longType + 0)

但Long不能超过最大值:)


1
+ 0转换没有任何效果,如果Java以类似于字符串的方式处理数字类型并置,则可能会起作用,但是由于您无缘无故进行添加操作,因此可能无法工作。
Juneone

-7

另一种解决方案可以是:

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

对于客户端正在执行POST且服务器DB仅了解Integer且客户端具有Long的情况,我已经尝试过这样做。


您将获得有关“实数长”值的NumberFormatException: Integer.valueOf(Long.MAX_VALUE.toString()); 导致java.lang.NumberFormatException: For input string: "9223372036854775807" 对Out-Of-Range-Exception的处理非常模糊,因为它现在的处理方式与对待包含字母的字符串相同。
Andreas

2
它也不会编译,因为您并不总是返回值。
Patrick M
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.