Java'da uzun süre int'e güvenle döküm


489

Bir döküm doğrulamak için Java en deyimsel yolu nedir longiçin intherhangi bir bilgi kaybetmez?

Bu benim mevcut uygulama:

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
İki kod yolu. Birincisi miras ve ints ihtiyacı var. Bu eski veriler bir int'e uymalıdır, ancak bu varsayım ihlal edilirse bir istisna atmak istiyorum. Diğer kod yolu uzun süreler kullanır ve oyuncu kadrosuna ihtiyaç duymaz.
Brigham

197
İnsanların ne yapmak istediğinizi neden yapmak istediğinizi her zaman nasıl sorguladıklarını seviyorum. Herkes bu sorularda tam kullanım durumunu açıklasaydı, kimse onları okuyamazdı, çok daha az cevap verirdi.
BT

24
BT - Bu nedenle çevrimiçi olarak herhangi bir soru sormaktan gerçekten nefret ediyorum. Yardım etmek istiyorsanız, bu harika, ama 20 soru oynamayın ve onları haklı çıkarmaya zorlamayın.
Mason240

59
BT ve Mason240 ile burada aynı fikirde değilsiniz: Soru sorucuya düşünmedikleri başka bir çözümü göstermek genellikle değerlidir. Kod kokularını işaretlemek yararlı bir hizmettir. 'Neden kendilerini merak ediyorum ...' dan 'onları kendilerini haklı göstermeye zorlamak' için uzun bir yol.
Tommy Herbert

13
Uzun dizilerle yapamayacağınız birçok şey vardır, örneğin bir dizini dizine eklemek.
skot

Yanıtlar:


580

Bunu yapmak için Java 8 ile yeni bir yöntem eklendi .

import static java.lang.Math.toIntExact;

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

ArithmeticExceptionTaşma durumunda atar .

Görmek: Math.toIntExact(long)

Diğer bazı taşma güvenli yöntemler Onlar ile sona Java 8. eklenmiştir kesin .

Örnekler:

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

5
Ayrıca addExactve var multiplyExact. Bölme ( MIN_VALUE/-1) ve mutlak değer ( abs(MIN_VALUE)) 'nin güvenli uygunluk yöntemleri olmadığına dikkat edilmelidir .
Aleksandr Dubinsky

Ancak Math.toIntExact(), normal döküm yerine kullanmanın farkı intnedir? Uygulanması Math.toIntExact()sadece atmalarını longiçin int.
Yamashiro Rion

@YamashiroRion Aslında toIntExact'in uygulanması önce dökümün taşmaya yol açıp açmayacağını kontrol eder, bu durumda bir ArithmeticException oluşturur. Yalnızca döküm güvenliyse, döndürdüğü uzuntan int'e döküm gerçekleştirir. Başka bir deyişle, int olarak temsil edilemeyen uzun bir sayı yayınlamaya çalışırsanız (örn. 2 147 483 647'nin üzerinde herhangi bir sayı), bir ArithmeticException oluşturur. Aynı işlemi basit bir yayın ile yaparsanız, sonuçta ortaya çıkan int değeriniz yanlış olur.
Pierre-Antoine

306

Sanırım bunu şöyle yaparım:

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

Sanırım bu amacı tekrarlanan dökümden daha açık bir şekilde ifade ediyor ... ama biraz öznel.

Potansiyel ilgi notu - C # 'da şu olurdu:

return checked ((int) l);

7
Her zaman menzil kontrolünü yapardım (!(Integer.MIN_VALUE <= l && l <= Integer.MAX_VALUE)). Başımı yapmanın başka yollarını bulmakta zorlanıyorum. Yazık Java yok unless.
Tom Hawtin - tackline

5
+1. Bu, tam olarak "istisnai durumlar için istisnalar kullanılmalıdır " kuralı kapsamına girer .
Adam Rosenfield

4
(Modern bir genel amaçlı dilde şöyle olurdu: "Eh? Ama ints keyfi büyüklüğe sahip mi?")
Tom Hawtin - tackline

7
@Tom: Kişisel tercih, sanırım - Mümkün olduğunca az olumsuzluk almayı tercih ediyorum. İstisna fırlatan bir gövdeye sahip bir "if" a bakarsam, istisnai görünmesini sağlayan koşulları görmek isterim - değerin "alt ucundan" gibi int.
Jon Skeet

6
@Tom: Bu durumda, negatifi kaldırır, döküm / dönüşü "if" gövdesinin içine koyarım ve sonra ne demek istediğimi görürseniz daha sonra bir istisna atarım.
Jon Skeet

132

Google Guava'nın Ints sınıfıyla yönteminiz şu şekilde değiştirilebilir:

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

Bağlı dokümanlardan:

checkedCast

public static int checkedCast(long value)

valueMümkünse eşit int değerini döndürür .

Parametreler: value - aralığındaki herhangi bir değer inttürü

Döndürür:int eşit olan değervalue

Atar: IllegalArgumentException - valuebüyük Integer.MAX_VALUEveya küçükseInteger.MIN_VALUE

Bu arada, safeLongToIntelbette kapsamlı bir yeniden düzenleme yapmadan işlevselliği değiştirmek için yerinde bırakmak istemiyorsanız , sargıya ihtiyacınız yoktur .


3
Guava's Ints.checkedCastOP'nin tam olarak ne yaptığını yapıyor
Parçalı Bulutlu

14
Guava çözümü için +1, ancak başka bir yöntemle sarmanıza gerek yok, sadece Ints.checkedCast(l)doğrudan arayın .
dimo414

8
Guava da Ints.saturatedCastbir istisna atmak yerine en yakın değeri döndürecek vardır .
Jake Walsh

Evet, varolan api benim durumum olarak, zaten projede kütüphane kullanmak güvenlidir: geçersiz ise istisna atmak: Ints.checkedCast (uzun) ve Ints.saturatedCast (uzun) uzun int dönüştürmek için en yakın almak.
2015'te Osify

29

BigDecimal ile:

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

Bunu beğendim, bu çözüme karşı kimse bir şey var mı?
Rui Marques

12
Bir BigDecimal tahsis etmek ve atmak, sadece bir fayda yöntemi olması gereken şeylere ulaşmak için, bu yüzden evet, bu en iyi süreç değil.
Riking

@Bu bağlamda, yeni bir örneğin gerekli olmadığını belirtmek BigDecimal.valueOf(aLong)yerine kullanmak daha iyidir new BigDecimal(aLong). Yürütme ortamının bu yöntemde önbelleğe alması gerekip gerekmediği, tıpkı Escape Analizinin olası varlığı gibi uygulamaya özgüdür. Çoğu gerçek yaşam durumunda, bunun performans üzerinde bir etkisi yoktur.
Holger

17

işte ihtiyaç duyduğunuzdan daha büyük olması durumunda değeri önemsememeniz durumunda bir çözüm;)

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

Görünüşe göre yanılıyorsun ... güzel sonra negatif çalışıyor. ayrıca, ne anlama geliyor too low? lütfen, kullanım örneği sağlayın.
Vitaliy Kulikov

Bu çözüm hızlı ve güvenli olanıdır, o zaman sonuca uymak için Long to Int'yi konuşuyoruz.
Vitaliy Kulikov

11

DONT: Bu bir çözüm değil!

İlk yaklaşımım:

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

Ancak bu sadece uzun süreyi bir int'e çevirir, potansiyel olarak yeni Longörnekler oluşturur veya bunları Long havuzundan alır.


Sakıncaları

  1. Long.valueOfLongsayı Longhavuz aralığında değilse yeni bir örnek oluşturur [-128, 127].

  2. intValueUygulama hiçbir şey birden yapar:

    return (int)value;

Bu yüzden sadece döküm daha kötü düşünülebilir longiçin int.


4
Yardım etme girişiminiz takdir ediliyor, ancak işe yaramayan bir şeye örnek vermek, işe yarayan bir çözüm sunmakla aynı şey değil. Doğru bir yol eklemek için düzenleme yaparsanız, bu oldukça iyi olabilir; Aksi takdirde, cevap olarak gönderilmesi gerçekten uygun değildir.
Pops

4
Tamam, neden hem ÇALIŞANLAR hem de DONT'lar olmasın? Tbh, bazen keşke böyle bir desen / kod kullanıp kullanmadığımı kontrol etmek için bir şeylerin (DONT) nasıl yapılamayacağına dair bir liste olmasını isterdim. Her neyse, bu "cevabı" silebilirim.
Andreas

1
İyi anti-desen. Her neyse, uzun değer int için aralık dışındaysa ne olacağını açıklarsanız harika olurdu? ClassCastException ya da bunun gibi bir şey olacak sanırım?
Peter Wippermann

2
@PeterWippermann: Biraz daha bilgi ekledim. Onları anlaşılabilir bir saygı olarak görüyor musunuz? yeterince açıklayıcı mı?
Andreas

7

Bir değeri kullanmanın değeri değiştirip değiştirmediğini görmenin bariz yolunun sonucu yayınlamak ve kontrol etmek olduğunu iddia ediyorum. Ancak karşılaştırırken gereksiz oyuncuları kaldıracağım. Ben de bir harf değişkeni isimleri çok istekli değilim (istisna xve y, ancak satır ve sütun (bazen sırasıyla) anlamına geldiğinde değil).

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

Ancak, gerçekten mümkünse bu dönüşümden kaçınmak istiyorum. Açıkçası bazen bu mümkün değildir, ancak bu durumlarda IllegalArgumentException, müşteri kodu söz konusu olduğunda, neredeyse kesinlikle yanlış istisna atmaktır.


1
Google Guava Ints :: checkingCast'in son sürümleri bunu yapar.
lexicalscope

2

Java tamsayı türleri imzalı olarak gösterilir. 2 31 ve 2 32 (veya -2 31 ve -2 32 ) arasında bir giriş olduğunda kadro başarılı olur, ancak testiniz başarısız olur.

Kontrol edilecek şey, yüksek bitlerinin longhepsinin aynı olup olmadığıdır :

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
İmzanın onunla ne ilgisi olduğunu anlamıyorum. Bir örnek verebilir değil kaybetmek bilgi ancak does testte başarısız? 2 ^ 31, Integer.MIN_VALUE (yani -2 ^ 31) 'e çevrilir, böylece bilgi kaybolur.
Jon Skeet

@Jon Skeet: Belki ben ve OP birbirimizle konuşuyoruz. (int) 0xFFFFFFFFve (long) 0xFFFFFFFFLfarklı değerlere sahipler, ancak her ikisi de aynı "bilgileri" içerir ve orijinal uzun değeri int'ten çıkarmak neredeyse önemsizdir.
mafya

0xFFFFFFFF yerine, başlangıç ​​için -1 olduğunda, orijinal uzun değeri int'den nasıl ayıklayabilirsiniz?
Jon Skeet

Ben net değilim eğer özür dilerim. Uzun ve int her ikisi de aynı 32 bit bilgi içeriyorsa ve 32. bit ayarlanmışsa, int değerinin uzun değerden farklı olduğunu söylüyorum ama uzun değer elde etmek kolaydır
mafya

@mob bu ne ile ilgilidir? OP'nin kodu,> 2 ^ {31} uzun değerlerinin girişlere yayınlanamayacağını doğru bir şekilde bildiriyor
Parçalı Bulutlu

0
(int) (longType + 0)

ama Uzun maksimum geçemez :)


1
+ 0O senin sebepsiz bir eklenti operasyon yapıyor olmadığından Java dizeleri benzer bir şekilde sayısal tip birleştirme tedavi, eğer, işe yarayabilecek bu dönüşüm için hiçbir şey ekler ama.
sixones

-7

Diğer bir çözüm şunlar olabilir:

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

İstemci bir POST yapıyor ve istemci bir uzun varken sunucu DB yalnızca tamsayılar anlıyor durumlarda bu denedim.


"Gerçek uzun" değerlerde bir NumberFormatException elde edersiniz: Integer.valueOf(Long.MAX_VALUE.toString()); sonuçta, java.lang.NumberFormatException: For input string: "9223372036854775807" harfleri içeren bir dize ile aynı şekilde işlendiği için, Aralık Dışı-İstisnayı hemen hemen gizler.
Andreas

2
Ayrıca derleme yapmaz çünkü her zaman bir değer döndürmezsiniz.
Patrick M
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.