Javaで安全にlongをintにキャストする


489

キャストからlongへのキャストがint情報を失わないことを確認するJavaの最も慣用的な方法は何ですか?

これは私の現在の実装です:

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
2つのコードパス。1つはレガシーであり、intが必要です。そのレガシーデータはすべてintに収まる必要がありますが、その前提に違反した場合は例外をスローします。他のコードパスはlongを使用し、キャストは必要ありません。
ブリガム

197
なぜあなたがやりたいことをしたいのか、人々がいつも疑問に思っているのが好きです。誰もがこれらの質問で完全なユースケースを説明した場合、だれもそれらを読むことができなくなり、はるかに少なく答えられます。
BT

24
BT-このため、オンラインで質問するのは本当に嫌いです。あなたが助けたいなら、それは素晴らしいことですが、20の質問をして彼らに彼ら自身を正当化することを強制しないでください。
Mason240

59
ここでBTとMason240に同意しない:質問者に彼らが考えていない別の解決策を示すことはしばしば価値があります。コードのにおいを報告することは便利なサービスです。それは、「なぜ私は興味があるのか​​...」から「正当化するように強制する」までの長い道のりです。
トミーハーバート

13
longを使用して実行できないことはたくさんあります。たとえば、配列のインデックスを作成します。
2013

回答:


580

それを行うための新しいメソッドが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
ともaddExactありmultiplyExactます。注目すべきは、除算(MIN_VALUE/-1)および絶対値(abs(MIN_VALUE))には安全な便利なメソッドがないことです。
Aleksandr Dubinsky 2017年

しかしMath.toIntExact()、通常のキャスト先の代わりに使用することの違いは何intですか?へのMath.toIntExact()単なるキャストの実装。longint
山城リオン

@YamashiroRion実際には、toIntExactの実装は、キャストがオーバーフローを引き起こすかどうかを最初にチェックします。その場合、ArithmeticExceptionがスローされます。キャストが安全な場合にのみ、次にlongからintへのキャストを実行します。つまり、intとして表現できない長い数値(たとえば、厳密に2 147 483 647を超える数値)をキャストしようとすると、ArithmeticExceptionがスローされます。単純なキャストで同じことを行うと、結果のint値は間違ったものになります。
ピエールアントワーヌ

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))ます。他の方法で頭を動かすのは難しいと思います。Pity Javaにはありませんunless
トム・ホーティン-

5
+1。これは、「例外は例外的な条件に使用する必要がある」という規則に正確に該当します。
Adam Rosenfield、

4
(現代の汎用言語では次のようになります:「え?しかし、intには任意のサイズがあるのですか?」)
トム・ホーティン-タックライン'19年

7
@トム:個人的な好みだと思います-ネガはできるだけ少なくしたいです。例外をスローするボディを持つ "if"を表示している場合、それが例外的に見える条件を確認したいと思いintます。
Jon Skeet、

6
@Tom:その場合、ネガを削除し、キャスト/リターンを「if」本体の中に入れ、その後、意味がわかる場合は例外をスローします。
Jon Skeet、

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
Guavaソリューションの+1 Ints.checkedCast(l)。別のメソッドでラップする必要はありませんが、直接呼び出すだけです。
dimo414 2013年

8
GuavaはInts.saturatedCast、例外をスローする代わりに、どれが最も近い値を返すかも持っています。
ジェイクウォルシュ

はい、既存のAPIを私のケースとして使用しても安全です。ライブラリはすでにプロジェクトにあります。無効な場合は例外をスローします。
Osify 2015年

29

BigDecimalの場合:

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

私はこれが好きです、誰かがこの解決策に対して何かを持っていますか?
Rui Marques

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 2013

このソリューションは高速で安全なソリューションであり、結果に従うためにLongをIntにキャストすることを話している。
Vitaliy Kulikov 2013

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
助けようとする試みは評価されますが、機能しない例を示すことは、機能するソリューションを提供することとはまったく異なります。正しい方法を追加するために編集する場合、これはかなり良いかもしれません。それ以外の場合は、回答として投稿することは本当に適切ではありません。
ポップス

4
わかりました、なぜDOとDONTの両方がないのですか?ええと、時々、私がそのようなパターン/コードを使用したかどうかを確認するために何をしないか(DONT)のリストが欲しいと思います。とにかく、この「答え」を削除できます。
Andreas

1
良いアンチパターン。とにかく、長い値がintの範囲外である場合に何が起こるかを説明できたらすばらしいでしょう。ClassCastExceptionまたはこのような何かがあると思いますか?
Peter Wippermann 2013年

2
@PeterWippermann:さらに情報を追加しました。あなたはそれらを理解しやすいものと考えますか?説明は十分ですか?
Andreas

7

値をキャストすることで値が変更されたかどうかを確認する明白な方法は、結果をキャストしてチェックすることだと私は主張します。ただし、比較するときに不要なキャストは削除します。また、1文字の変数名にはあまり熱心ではありません(例外xとはy、行と列を意味する場合は(それぞれ))。

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が行うことです。
レキシカルスコープ

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値を抽出することはほとんど簡単です。
mob

longが0xFFFFFFFFではなく-1であった可能性がある場合、intから元のlong値をどのように抽出できますか?
Jon Skeet、

わかりません。申し訳ありません。longとintの両方に同じ32ビットの情報が含まれていて、32番目のビットが設定されている場合、int値はlong値とは異なりますが、長い値を取得するのは簡単です
mob

@mobこれは何を参照していますか?OPのコードは、長い値> 2 ^ {31}をintにキャストできないことを正しく報告しています
部分的に曇り

0
(int) (longType + 0)

ただし、Longは最大値を超えることはできません。


1
+ 0Javaは、文字列と同様に数値型の連結を処理する場合、それはあなたが理由もなく追加操作を行っていないので、それがうまくいくかもしれない、この変換には何も追加しません。
Sixones

-7

他の1つの解決策は次のとおりです。

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

クライアントがPOSTを実行していて、クライアントがLongを持っているときにサーバーDBが整数しか理解できない場合に、これを試しました。


「実際に長い」値でNumberFormatExceptionが発生します。これ Integer.valueOf(Long.MAX_VALUE.toString()); によりjava.lang.NumberFormatException: For input string: "9223372036854775807" 、文字を含む文字列と同じ方法で処理されるため、Out-Of-Range-Exceptionがほとんど難読化されます。
Andreas

2
また、常に値を返すわけではないため、コンパイルも行われません。
Patrick M
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.