Попытка сгенерировать случайный текст на Java — «Несоответствие типов: не удается преобразовать из long в int»

#java #random

Вопрос:

Я пытаюсь сгенерировать 100 символов случайного текста на Java. Вот мой код:

 import java.lang.Math;

public class RandomTextGenerator {
    public static void main(String[] args) {
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
        for (int i = 0; i < 100; i  ) {
        int a = Math.round(62*Math.random());
        int b = a   1;
        System.out.println(characters.substring(a,b));
    }
}
 

Я получаю ошибку «Несоответствие типа: не удается преобразовать из long в int».

Но я этого не понимаю. Я четко заявил a и b как int , нет long . Я ожидаю, что мой код выдаст мне случайное целое число от 0 до 61, поэтому оно не должно автоматически быть a long , потому что 0 и 61 находятся в пределах диапазона целых чисел.

Я довольно новичок в Java, поэтому приношу извинения, если делаю что-то явно неправильное.

Комментарии:

1. Math.random возвращает a double , поэтому используется перегрузка Math.round возврата a long . И поскольку ваши переменные меньше, вы получаете ошибку

2. меняйся int A и int B на long A и long B и с тобой все должно быть в порядке

3. Я уже пробовал заменить int A и int B на длинные A и длинные B, это не сработало, потому что .substring() ожидает ввода.

4. Думаю, теперь я понимаю, в чем причина проблемы. Спасибо, нечестивый! Но как мне это исправить?

5. Кстати, соглашения об именовании Java гласят, что имена переменных должны быть в нижнем регистре, a amp; b . Более читабельно.

Ответ №1:

Math.round() здесь возвращается долго, поэтому вы можете ввести его, написав так,

 int A = (int) Math.round(62*Math.random());
 

Другой способ сделать это (но неэффективный)

 int A = Long.valueOf(Math.round(62*Math.random())).intValue();
 

В остальном вы все делаете правильно. 🙂

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

Ответ №2:

Рассмотрите возможность использования класса java.util.Случайным образом для генерации случайных int s.

 String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
java.util.Random rand = new java.util.Random();
for (int i = 0; i < 100; i  ) {
    int a = rand.nextInt(62);
    int b = a   1;
    System.out.println(characters.substring(a,b));
}
 

Ответ №3:

Смешивание типов влияет на ваши результаты

Ты сказал:

Я ожидаю, что мой код выдаст мне случайное целое число от 0 до 61

Ваши ожидания неверны.

Сначала вы смешиваете типы в этих круглых скобках: (62*Math.random()) . Слева находится an int , как предполагает компилятор. (См. Спецификацию языка Java о литералах.) Справа находится вызов Math.random() , который задокументирован как возврат a double . Что происходит, когда вы умножаете an int на a double в Java? В результате получается а double .

Затем вы передаете это double значение в своем вызове Math.round . Изучите Javadoc. Вы найдете два названных метода round , каждый из которых принимает другой тип параметра. И каждый возвращает другой тип. Метод Math.round(double a) возвращает a long , в то время как другой Math.round возвращает an int .

Вы бы избежали этого обмана, сохранив свой код предельно простым, а не вложив операции в строку кода. Вы бы постепенно объявляли каждый этап вычисления, чтобы быть в курсе типа данных каждого возвращаемого значения. Пошаговые результаты облегчают отладку. И, в качестве бонуса, простой код, скорее всего, будет сильно оптимизирован компилятором.

Кроме того, вы можете позвонить Math.toIntExact , чтобы преобразовать a long в an int , когда будете уверены, что значение подойдет. Этот метод создает исключение, если значение переполняет значение an int . Таким образом, вы будете предупреждены, если ваши ожидания относительно подгонки окажутся неверными.

Кроме того, ваш код будет намного легче понять, если вы будете избегать магических чисел. Вместо этого используйте хорошо названные переменные/константы. Например, я не сразу понял, что ваш 62 множитель-это длина вашего пула символов. И еще один момент: такой номер должен быть программно закодирован с возможностью вызова String#length .

Еще одна проблема: ваш код прерывается, если начальная позиция в пуле оказывается последним символом в пуле. Затем добавление одного из них приведет вас к концу, что приведет к ошибке во время выполнения. Так что вычтите один из вашего лимита 62 , чтобы получить 61 . Или в приведенном ниже коде, ( POOL_OF_CHARACTERS.length() - 1 ) .

Собрав все это вместе, вы можете получить код, подобный этому:

 final String POOL_OF_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
final int DESIRED_LENGTH_OF_RESULTING_TEXT = 100;
StringBuilder stringBuilder = new StringBuilder( "" );
for ( int i = 0 ; i < DESIRED_LENGTH_OF_RESULTING_TEXT ; i   )
{
    double randomProduct = ( ( POOL_OF_CHARACTERS.length() - 1 ) * Math.random() );
    long intermediateResult = Math.round( randomProduct );
    int startingPositionInPool = Math.toIntExact( intermediateResult ); // Throws an exception if the value overflows an int.
    int endingPositionInPool = ( startingPositionInPool   1 );
    String piece = POOL_OF_CHARACTERS.substring( startingPositionInPool , endingPositionInPool );
    stringBuilder.append( piece );
}
String randomString = stringBuilder.toString();
System.out.println( "randomString = "   randomString );
 

Когда бегут.

Случайная строка = 7YqRnJ4TXzMo3nvoUkBRQYq5evOhBjo3I2Cc9fTWAfCFlGY5R4rlFjWqqwRqERkVylXI7le4VLs5nKBHlNuHb03qxFeebDBSsHab

Кстати, я бы заменил Math.random на более удобный ThreadLocalRandom#nextInt( int origin , int bound ) . Обратите внимание, что этот метод полуоткрыт, причем начало является включающим, а окончание-исключительным. Так что у нас не будет проблем с выходом за пределы бассейна. Нам не нужно вычитать один.

 final String POOL_OF_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
final int DESIRED_LENGTH_OF_RESULTING_TEXT = 100;
StringBuilder stringBuilder = new StringBuilder( "" );
for ( int i = 0 ; i < DESIRED_LENGTH_OF_RESULTING_TEXT ; i   )
{
    int startingPositionInPool = ThreadLocalRandom.current().nextInt( 0 , POOL_OF_CHARACTERS.length() );  // Half-Open. The specified bound is exclusive.
    int endingPositionInPool = ( startingPositionInPool   1 );  
    String piece = POOL_OF_CHARACTERS.substring( startingPositionInPool , endingPositionInPool );
    stringBuilder.append( piece );
}
String randomString = stringBuilder.toString();
System.out.println( "randomString = "   randomString );
 

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

 final long DESIRED_LENGTH_OF_RESULTING_TEXT = 100;
int[] poolOfCodePoints = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890".codePoints().toArray();
IntStream positions = new Random().ints( DESIRED_LENGTH_OF_RESULTING_TEXT , 0 , poolOfCodePoints.length );
IntStream codePoints = positions.map( indexIntoCodePoints -> poolOfCodePoints[ indexIntoCodePoints ] );
StringBuilder stringBuilder = codePoints.collect( StringBuilder :: new , StringBuilder :: appendCodePoint , StringBuilder :: append );
String randomText = stringBuilder.toString();
 

Или, короче:

 final long DESIRED_LENGTH_OF_RESULTING_TEXT = 100;
int[] poolOfCodePoints = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890".codePoints().toArray();
String randomText =
        new Random()
                .ints( DESIRED_LENGTH_OF_RESULTING_TEXT , 0 , poolOfCodePoints.length )
                .map( indexIntoCodePoints -> poolOfCodePoints[ indexIntoCodePoints ] )
                .collect( StringBuilder :: new , StringBuilder :: appendCodePoint , StringBuilder :: append )
                .toString();