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

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


一种新的方法被添加到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〕


我想我会这么简单:

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


使用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包装器,除非您希望将其保留在适当的位置,以便在不进行大量重构的情况下更改功能。


使用BigDecimal:

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


这里有一个解决方案,如果你不关心价值,如果价值大于需要的话;)

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


不要:这不是解决办法!

我的第一个方法是:

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

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

缺点

  • 如果数字不在long的池范围内,Long.valueOf创建一个新的long实例。

  • intValue的实施只不过是:

    1
    return (int)value;

  • 所以这比把long铸造成int更糟糕。


    我声称,查看投射值是否改变值的明显方法是投射并检查结果。不过,我会在比较时去掉不必要的演员表。我也不太喜欢一个字母的变量名(xy除外,但它们的意思不是行和列(有时分别是行和列)。

    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几乎肯定是错误的异常。


    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("...");
        }
    }


    1
    (int) (longType + 0)

    但long不能超过最大值:)


    另一种解决方案是:

    1
    2
    3
    4
    5
    6
    7
    8
    public int longToInt(Long longVariable)
    {
        try {
                return Integer.valueOf(longVariable.toString());
            } catch(IllegalArgumentException e) {
                   Log.e(e.printstackstrace());
            }
    }

    我尝试过在客户机执行POST并且服务器数据库只理解整数而客户机具有长整型的情况下使用这种方法。