Safely casting long to int in Java
Java中最惯用的方法是如何验证从EDCOX1(0)到EDCOX1(1)的转换不会丢失任何信息?
这是我当前的实现:
1 2 3 4 5 6 7
| 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 ;
} |
- 我很好奇你为什么要这么做。如果你有一个long,为什么你不能只使用一个long?那样的话,你就不用担心了。
- 两条代码路径。一个是遗产,需要Ints。这个遗留数据应该都适合一个int,但是如果违反了这个假设,我想抛出一个异常。另一个代码路径将使用long,不需要强制转换。
- 我喜欢人们总是问你为什么要做你想做的事。如果每个人都在这些问题中解释了他们的完整用例,就没有人能够阅读它们,更不用说回答它们了。
- 因为这个原因,我真的不喜欢在网上问任何问题。如果你想帮忙,那太好了,但不要提出20个问题,强迫他们自己辩护。
- 在这里不同意B T和MASON240:向提问者展示另一个他们没有想到的解决方案通常很有价值。标记代码气味是一项有用的服务。从"我很好奇为什么……"到"强迫他们为自己辩护",还有很长的路要走。
- 有很多事情你不能用long做,例如索引一个数组。
- @汤米赫伯特,究竟如何安全地将long转换成代码气味?
- @Pacrier:一般来说,如果你需要的是一个in t,-斜线-如果你需要的是一个long,你不应该把long放在第一位。(记住,"代码气味"不是指"坏代码",而是指"表明可能有问题的代码"。)
- 虽然@bt所说的是正确的,但是询问为什么你想做一件特别的事情只是为了确保没有一个更简单的解决方案不需要提问者试图做的任何事情,这可能是有用的。考虑这个例子:我有一个问题Q,我想了一个方法M来解决它,我陷入了M中的P部分。但是可能有一个替代的解决方案A,它根本不需要P,而且可能更有效。因此,了解问题的总体要点总是一个好主意。
一种新的方法被添加到Java 8中。
1 2 3 4
| import static java. lang. Math. toIntExact;
long foo = 10L ;
int bar = toIntExact (foo ); |
号
如果发生溢出,将抛出一个ArithmeticException。
见:Math.toIntExact(long)。
在Java 8中添加了其他几个溢出安全方法。它们以精确结尾。
示例:
- 埃多克斯1〔14〕
- 埃多克斯1〔15〕
- 江户十一〔16〕号
- 埃多克斯1〔17〕
- 埃多克斯1〔18〕
- 我们还有addExact和multiplyExact。值得注意的是,除法(MIN_VALUE/-1和绝对值(abs(MIN_VALUE)没有安全方便的方法。
- 但是使用Math.toIntExact()而不是通常使用的int有什么区别?Math.toIntExact()的实施只是把long抛给int而已。
- @Yamashirorion实际上首先检查ToIntext的实现是否会导致溢出,在这种情况下,它会抛出算术异常。只有当强制转换是安全的,它才会执行从long到int的强制转换,并返回到int。换言之,如果您试图转换一个不能表示为int的长数字(例如任何严格高于2147483647的数字),它将抛出算术异常。如果对简单的强制转换执行相同的操作,则生成的int值将是错误的。
我想我会这么简单:
1 2 3 4 5 6 7
| 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它将只是:
1
| return checked ((int) l); |
号
- 我总是像(!(Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE))那样进行范围检查。我发现很难用其他方法来解决这个问题。遗憾的是Java没有EDCOX1 1。
- + 1。这完全符合"例外情况应用于例外情况"规则。
- (用现代通用语言来说,它应该是:"嗯?但是ints的大小是任意的?")
- @汤姆:我想是个人喜好吧-我喜欢尽量少带底片。如果我看的是一个带有抛出异常的主体的"if",我想看看使它看起来异常的条件——比如int的值是"离底端"。
- 我从白名单的角度来看。这些是我感兴趣的价值观。语言设计迫使否定。
- @汤姆:如果那样的话,我会去掉底片,把铸币/退换币放在"如果"的身体里,然后再抛出一个例外,如果你明白我的意思的话。
- 我建议使用算术异常来匹配java8 math.tointexact(…)的接口。
使用google guava的ints类,您的方法可以更改为:
1 2 3
| public static int safeLongToInt(long l) {
return Ints.checkedCast(l);
} |
从链接的文档:
checkedCast
public static int checkedCast(long value)
Returns the int value that is equal to value, if possible.
Parameters:
value - any value in the range of the int type
Returns:
the int value that equals value
Throws:
IllegalArgumentException - if value is greater than Integer.MAX_VALUE or less than Integer.MIN_VALUE
号
顺便说一下,您不需要safeLongToInt包装器,除非您希望将其保留在适当的位置,以便在不进行大量重构的情况下更改功能。
- 瓜娃的Ints.checkedCast正好和OP一样,顺便说一下。
- +1对于guava解决方案,虽然不需要用其他方法包装它,但是直接调用Ints.checkedCast(l)。
- guava也有Ints.saturatedCast,它将返回最接近的值,而不是抛出异常。
- 是的,使用现有的API作为我的例子是安全的,项目中已经存在的库:如果无效则抛出异常:int s.checkedcast(long)和ints.saturatedcast(long),以获取将long转换为int的最近的。
使用BigDecimal:
1 2 3
| long aLong = ... ;
int anInt = new BigDecimal(aLong ). intValueExact(); // throws ArithmeticException
// if outside bounds |
- 我喜欢这个,有人反对这个解决方案吗?
- 好吧,它分配并丢弃一个bigdecimal,只是为了得到一个实用方法,所以是的,这不是最好的过程。
- @在这方面,最好使用BigDecimal.valueOf(aLong),而不是new BigDecimal(aLong),来表示不需要新的实例。执行环境是否在该方法上进行缓存,是特定于实现的,就像可能存在的转义分析一样。在大多数现实生活中,这对性能没有影响。
这里有一个解决方案,如果你不关心价值,如果价值大于需要的话;)
1 2 3
| public static int safeLongToInt (long l ) {
return (int) Math. max(Math. min(Integer. MAX_VALUE, l ), Integer. MIN_VALUE);
} |
。
- 看来你错了…它会很好地工作然后是消极的。另外,too low是什么意思?请提供用例。
- 这是一个快速而安全的解决方案,那么我们就要和施特龙谈谈,要服从结果。
- 既然math.min/max接受long,它应该是正常的。
不要:这不是解决办法!
我的第一个方法是:
1 2 3
| public int longToInt (long theLongOne ) {
return Long. valueOf(theLongOne ). intValue();
} |
但这只是将long转换成int,可能会创建新的long实例或从long pool中检索它们。
缺点
如果数字不在long的池范围内,Long.valueOf创建一个新的long实例。
intValue的实施只不过是:
。
所以这比把long铸造成int更糟糕。
- 感谢您的帮助,但是举一个不起作用的例子和提供一个有效的解决方案是不一样的。如果你想编辑添加一个正确的方法,这可能是相当好的;否则,它并不真正适合作为答案发布。
- 好吧,为什么不同时拥有dos和donts呢?但是,有时候我希望我有一个不做事情(donts)的列表来检查我是否使用了这样的模式/代码。无论如何,我可以删除这个"答案"。
- 良好的反模式。不管怎样,如果你解释了长值超出了int的范围会发生什么,那就太好了。我想会有一个类的例外或者类似的东西?
- @彼得维伯曼:我增加了一些信息。你认为他们可以理解吗?解释得够多了?
- 好的,谢谢:—)
我声称,查看投射值是否改变值的明显方法是投射并检查结果。不过,我会在比较时去掉不必要的演员表。我也不太喜欢一个字母的变量名(x和y除外,但它们的意思不是行和列(有时分别是行和列)。
1 2 3 4 5 6 7 8 9
| 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的最新版本所做的。
Java整数类型表示为已签名。如果输入介于231和232(或-231和-232)之间,则转换将成功,但测试将失败。
要检查的是long的所有高位是否都相同:
1 2 3 4 5 6 7 8
| 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_值(即-2^31),因此信息已丢失。
- @乔恩·斯基特:也许我和剧组的人正在互相交谈。(int) 0xFFFFFFFF和(long) 0xFFFFFFFFL具有不同的值,但它们都包含相同的"信息",从int中提取原始长值几乎是微不足道的。
- 当long可以是-1而不是0xffffff时,如何从int中提取原始long值?
- 对不起,如果我不清楚。我是说,如果long和int都包含相同的32位信息,并且如果设置了32位,那么int值与long值不同,但是很容易获得long值。
- @暴民这是指什么?OP的代码正确地报告长值>2^ 31不能强制转换为整数。
- 在爪哇,任何原始类型都可以被铸造成int型。
。
但long不能超过最大值:)
- EDCOX1(0)对这个转换不加任何作用,如果Java以类似于字符串的方式处理数值类型连接,它可能会起作用,但因为它没有为你做一个无缘无故的添加操作。
另一种解决方案是:
。
我尝试过在客户机执行POST并且服务器数据库只理解整数而客户机具有长整型的情况下使用这种方法。
- 性能嗅觉测试不合格。将数字转换为字符串,然后返回?????
- 您将在"实际长"值上得到一个数字格式异常:Integer.valueOf(Long.MAX_VALUE.toString());导致java.lang.NumberFormatException: For input string:"9223372036854775807"使超出范围的异常变得非常模糊,因为它现在的处理方式与处理包含字母的字符串的处理方式相同。
- 它也不会编译,因为您不总是返回值。