2016-09-13 8 views
6

Posiadanie reprezentacji łańcuchowej liczby (bez miejsc po przecinku), jaki jest najlepszy sposób przekonwertowania jej na jeden z java.lang.Integer lub java.lang.Long lub java.math.BigInteger? Jedynym warunkiem jest to, że typ konwertowany powinien być typu minimalnego wymaganego do przechowywania numeru.Konwersja reprezentacji ciągu do minimalnego obiektu numerycznego

Mam bieżące wdrożenie, które działa dobrze, ale chciałbym wiedzieć, czy istnieje lepszy kod bez obsługi wyjątków.

package com.stackoverflow.programmer; 

import java.math.BigInteger; 

public class Test { 
    public static void main(String[] args) { 

     String number = "-12121111111111111"; 
     Number numberObject = null; 
     try { 
      numberObject = Integer.valueOf(number); 
     } catch (NumberFormatException nfe) { 
      System.out.println("Number will not fit into Integer type. Trying Long..."); 
      try { 
       numberObject = Long.valueOf(number); 
      } catch (NumberFormatException nfeb) { 
       System.out.println("Number will not fit into Long type. Trying BigInteger..."); 
       numberObject = new BigInteger(number); 
      } 
     } 
     System.out.println(numberObject.getClass() + " : " 
       + numberObject.toString()); 
    } 
} 
+0

Tak, BigInteger zadziała –

+0

BigInteger będzie działać? Ale nie potrzebuję obiektu BigInteger, jeśli wejściowy ciąg znaków może zostać umieszczony w klasie Integer lub Long. –

+0

Możesz uzyskać długość łańcucha, a następnie wyciągnąć wnioski na podstawie tej długości. Z pewnością nie jest to najlepsze podejście, a zamiast tego po prostu użyłbym BigIntegera. –

Odpowiedz

4

Z tego, co pan powiedział, tutaj jest to, co bym zrobił:

import java.math.BigInteger; 
import java.util.Arrays; 
import java.util.List; 

public class TestSO09_39463168_StringToMinimalNumber { 

    public static void main(String[] args) { 
     List<String> strNumbers = Arrays.asList("0", //int 
       "123", //int 
       "-456", //int 
       "2147483700", // Long 
       "-2147483700", // Long 
       "9223372036854775900", //BigInt 
       "-9223372036854775900" //BigInt 
       ); 

     for(String strNumber : strNumbers){ 
      Number number = stringToMinimalNumber(strNumber); 
      System.out.println("The string '"+strNumber+"' is a "+number.getClass()); 
     } 

    } 

    public static Number stringToMinimalNumber(String s){ 
     BigInteger tempNumber = new BigInteger(s); 

     if(tempNumber.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 || tempNumber.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0){ 
      return tempNumber; 
     } else if(tempNumber.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0 || tempNumber.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0){ 
      return tempNumber.longValue(); //Autobox to Long 
     } else { 
      return tempNumber.intValue(); //Autobox to Integer 
     } 

    } 

} 

Musisz użyć tymczasowego BigInteger, albo skończysz z Lazarov-tych rozwiązanie, które jest poprawne, ale tak naprawdę nie można zrobić czegoś takiego z powodów wymienionych w komentarzach.

W każdym razie, każdy BigInteger (te, które nie zostaną zwrócone) będzie zbiorem śmieci. Jeśli chodzi o autoboxing, nie sądzę, że jest to coś złego. Możesz także uczynić "BigInteger.valueOf(Long.MAX_VALUE))" jako stałą. Może kompilator lub JVM zrobi to samodzielnie.

Nie jestem pewien, na ile jest efektywny, a używanie tylko BigIntegera może być dobrym pomysłem (jak zauważył Spotted), ponieważ wątpię, czy naprawdę poprawiłoby to resztę kodu, aby użyć odpowiedniego rozmiaru, i może nawet być podatny na błędy, jeśli spróbujesz użyć tych Liczb ze sobą ... Ale znowu, wszystko zależy od tego, czego potrzebujesz. (I tak, przy użyciu wyjątek jak kontrola przepływu jest naprawdę zły pomysł, ale można dodać połów przymierzyć BigInteger tempNumber = new BigInteger(s); rzucić swój własny wyjątek, jeśli s nie jest liczbą w ogóle)

Dla celów rekreacyjnych, zrobiłem rozwiązanie bez użycia BigInteger i tylko z ciągiem parsowania (to jeszcze nie jest to, co polecam zrobić, ale to było zabawne :)

public static final String INT_MAX_VALUE = "2147483647"; 
public static final String LONG_MAX_VALUE = "9223372036854775807"; 

public static Number stringToMinimalNumberWithoutBigInteger(String numberStr){ 
    //Removing the minus sign to test the value 
    String s = (numberStr.startsWith("-") ? numberStr.substring(1,numberStr.length()) : numberStr); 

    if(compareStringNumber(s, LONG_MAX_VALUE) > 0){ 
     return new BigInteger(numberStr); 
    } else if(compareStringNumber(s, INT_MAX_VALUE) > 0){ 
     return new Long(numberStr); 
    } else { 
     return new Integer(numberStr); 
    } 
} 

//return postive if a > b, negative if a < b, 0 if equals; 
private static int compareStringNumber(String a, String b){ 
    if(a.length() != b.length()){ 
     return a.length() - b.length(); 
    } 
    for(int i = 0; i < a.length(); i++){ 
     if(a.codePointAt(i) != b.codePointAt(i)){ //Or charAt() 
      return a.codePointAt(i) - b.codePointAt(i); 
     } 
    } 
    return 0; 
} 
+1

Dzięki za ten wpis. Ten kod jest czysty. Przyjmę to jako odpowiedź. –

0

Cóż, jeśli chcesz to zrobić "ręcznie" spróbować czegoś takiego:

Definiujemy wartości max jako ciągi:

String intMax = "2147483647"; 
String longMax = "9223372036854775807"; 

i nasz numer:

String ourNumber = "1234567890" 

Teraz nasza logika będzie prosta: Sprawdzimy długości ciągów po pierwsze

  1. Jeśli nasze numery długość < int długość max: TO INT

  2. Jeśli długość nasze numery == długość int max: Sprawdź czy to INT lub LONG

  3. Jeśli nasze długość numery> int długość max:

    3.1 Jeśli nasze numery długość < długo długość max: TO LONG

    3.2 Jeśli nasz nu Długość mbers == długość długo max: Sprawdź to jest długi lub BIG INTEGER

    3.3 Jeśli nasze numery długość> długo długość max: TO DUŻY INTEGER

Kod powinien wyglądać mniej więcej tak (mam nie próbowałem skompilować może mieć składnię lub inne błędy):

if(ourNumber.lenght() < intMax.length){ 
    System.out.println("It is an Integer"); 
} else if(ourNumber.lenght() == intMax.length){ 
    // it can be int if the number is between 2000000000 and 2147483647 
      char[] ourNumberToCharArray = ourNumber.toCharArray(); 
      char[] intMaxToCharArray = intMax.toCharArray(); 
      int diff = 0; 
      for(int i = 0; i < ourNumberToCharArray.length; i++) { 
       diff = Character.getNumericValue(intMaxToCharArray[i]) - Character.getNumericValue(ourNumberToCharArray[i]); 
       if(diff > 0) { 
        System.out.println("It is a Long"); 
        break;     
       } else if(diff < 0) { 
        System.out.println("It is an Integer"); 
         break; 
       } 
      } 
      if(diff == 0){ 
       System.out.println("It is an Integer"); 
      } 
} else { 
    if(ourNumber.lenght() < longMax.length()) { 
     System.out.println("It is a Long"); 
    } else if(ourNumber.lenght() == longMax.length()){ 
      char[] ourNumberToCharArray = ourNumber.toCharArray(); 
      char[] longMaxToCharArray = longMax.toCharArray(); 
      int diff = 0; 
      for(int i = 0; i < ourNumberToCharArray.length; i++) { 
       diff = Character.getNumericValue(longMaxToCharArray[i]) - Character.getNumericValue(ourNumberToCharArray[i]); 
       if(diff > 0) { 
        System.out.println("It is a BigInteger"); 
        break;     
       } else if(diff < 0) { 
        System.out.println("It is a Long"); 
         break; 
       } 
      } 
      if(diff == 0){ 
       System.out.println("It is a Long"); 
      } 
    } else { 
     System.out.println("It is a BigInteger"); 
    } 
} 

Wtedy logika, która sprawdza czy numery mecz, czy też nie jest taka sama w obu przypadkach można jednak w zależności na przykład.

+2

Nie sądziłem, że możliwe jest bardziej skomplikowane rozwiązanie niż OP ... – Spotted

+0

Tak, skomplikowane, ale taniej, wierzcie lub nie. –

+0

Ten kod będzie bardzo drogi w utrzymaniu (dla tego, co robi), nieprofesjonalne jest dopuszczenie takiego kodu do produkcji. – Spotted

1

Proszę nie używać wyjątków do sterowania przepływem, jest to serious anti-pattern (również here).

Jak wymieniono w uwag prawdziwy rzecz, którą poproszono jest konwersja List<String> w List<Number>. Ponadto, jeśli dobrze rozumiem, wiesz, że:

  • powinny wystąpić tylko liczby bez miejsc po przecinku
  • Największą wartość można napotkać jest ewentualnie niezwiązanego

Na tej podstawie następującej metody wykona pracę w bardziej sprytny sposób:

private static List<Number> toNumbers(List<String> strings) { 
    return strings.stream() 
        .map(BigInteger::new) 
        .collect(Collectors.toList()); 
} 

Eidt: jeśli nie jesteś zaznajomiony z pojęciem strumienia, oto równoważny kod bez strumieni:

private static List<Number> toNumbers(List<String> strings) { 
    List<Number> numbers = new ArrayList<>(); 
    for (String s : strings) { 
     numbers.add(new BigInteger(s)); 
    } 
    return numbers; 
} 
+0

Jestem nowy w tej koncepcji strumieni. Ta metoda zawsze zwróci listę ? –

+0

@Programmer Z zewnątrz ta metoda zwraca 'List ' (tak, jak prosiłeś). Prawdziwym typem każdego elementu będzie jednak 'BigInteger'. Zaktualizowałem swoją odpowiedź równoważnym kodem bez używania strumieni (jeśli to może ci pomóc). – Spotted

+1

Myślę, że OP stwierdził również, że "przekonwertowany typ powinien być minimalnego typu danych wymaganego do przechowywania numeru.", Więc nie sądzę, że robienie BigInteger za każdym razem jest dobrym pomysłem. – Asoub

Powiązane problemy