2016-09-13 3 views
6

Имея строковое представление числа (без десятичных знаков), что является лучшим способом конвертировать его в любой из java.lang.Integer или java.lang.Long или java.math.BigInteger? Единственное условие заключается в том, что преобразованный тип должен иметь минимальный тип данных, необходимый для хранения номера.Преобразование строкового представления в минимальное число Объект

У меня эта текущая реализация, которая отлично работает, но я хотел бы знать, есть ли лучший код без обработки исключений.

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

Да, BigInteger будет работать –

+0

BigInteger будет работать? Но мне не нужен объект BigInteger, если входная строка может быть размещена внутри Integer или Long. –

+0

Вы можете получить длину строки и затем сделать выводы на основе этой длины. Это, конечно, не лучший подход, и я бы просто использовал BigInteger. –

ответ

4

Из того, что вы сказали, вот что я сделал бы:

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 
     } 

    } 

} 

Вы должны использовать временный BigInteger, иначе вы будете в конечном итоге с Lazarov годов решение, которое является правильным, но вы не можете сделать что-то подобное по причине, упомянутой в комментариях.

В любом случае, каждый BigInteger (тот, который не возвращается) будет собираться мусором. Что касается автобоксинга, я не думаю, что это плохо. Вы также можете сделать «BigInteger.valueOf(Long.MAX_VALUE))» постоянной. Возможно, компилятор или JVM сделают это самостоятельно.

Я не уверен, насколько он эффективен, и использование только BigInteger может быть хорошей идеей (как Пятнистый сделал), потому что я serioulsy сомневаюсь, что это действительно улучшит остальную часть вашего кода, чтобы использовать правильный размер, и это может быть даже подвержено ошибкам, если вы попытаетесь использовать эти Числа друг с другом ... Но опять же все зависит от того, что вам нужно. (и да, использование Exception в качестве управления потоком - это действительно плохая идея, но вы можете добавить попытку поймать на BigInteger tempNumber = new BigInteger(s);, чтобы выбросить свое собственное исключение, если s не является номером вообще)

В целях отдыха я сделал решение без использования BigInteger, и только строки разбора (это еще не то, что я Recommand сделать, но это было весело :)

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

Спасибо за сообщение. Этот код чист. Я приму это как ответ. –

0

Ну, если вы хотите сделать это "вручную" попробовать что-то вроде этого:

Определим значения макс как строки:

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

и наш номер:

String ourNumber = "1234567890" 

Теперь наша логика будет простой: Мы проверим длины строк, во-первых,

  1. Если наши номера длина < INT Максимальная длина: ОНО INT

  2. Если длина наши номера == Длина ИНТ макс: Проверьте это INT или LONG

  3. Если наши номера длина> INT максимальная длина:

    3,1 Если наши номера длина < долго максимальная длина: длинный

    3.2 Если наши числа л ength == Длина длиной не более: Проверьте это LONG или BIG INTEGER

    3,3 Если наши номера длина> долго максимальная длина: ЭТО БОЛЬШОЙ INTEGER

Код должен выглядеть примерно так (я не имею пытался скомпилировать это может иметь синтаксис или другие ошибки):

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

Тогда логика, которая проверяет, является ли число матч или не является одинаковым в обоих случаях вы можете, но в функции, например.

+2

Я не думал, что было возможно иметь более сложное решение, чем у OP ... – Spotted

+0

Да, сложно, но дешевле верить в это или нет. –

+0

Этот код будет очень дорогим для обслуживания (для чего он это делает), непрофессионально, чтобы такой код вышел в производство. – Spotted

1

Пожалуйста, не используйте исключения для управления потоком, это serious anti-pattern (также here).

Как упоминалось в комментариях реальная вещь, которую вы попросили, чтобы преобразовать List<String> в List<Number>. Кроме того, если я правильно понимаю, вы знаете, что:

  • Вы должны столкнуться только цифры без десятичных знаков
  • Самое большое значение, которое вы можете столкнуться, возможно, несвязанный

Исходя из этого, следующим способом сделает работу более умным способом:

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

Eidt: если вы не очень хорошо знакомы с понятием потока, вот эквивалент код без потоков:

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

Новая концепция этого потока. Этот метод всегда будет возвращать список ? –

+0

@Programmer С внешней стороны этот метод возвращает «Список » (как вам было предложено). Реальный тип каждого элемента будет «BigInteger». Я обновил свой ответ с помощью эквивалентного кода без использования потоков (если это может вам помочь). – Spotted

+1

Я думаю, что OP также заявила, что «преобразованный тип должен иметь минимальный тип данных, необходимый для хранения номера». Поэтому я не думаю, что создание BigInteger каждый раз - хорошая идея. – Asoub

Смежные вопросы