Home > Enterprise >  Trying to generate random text in Java - "Type mismatch: cannot convert from long to int"
Trying to generate random text in Java - "Type mismatch: cannot convert from long to int"

Time:10-19

I'm trying to generate 100 characters of random text in Java. Here is my code:

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

I get the error "Type mismatch: cannot convert from long to int".

But I don't understand this. I clearly declared a and b as int, not long. I'm expecting my code to give me a random integer from 0 to 61 so it shouldn't automatically be a long because 0 and 61 are well within the range of integers.

I am fairly new to Java so apologies if I'm doing something obviously wrong.

CodePudding user response:

Math.round() is returning long here, so you can type cast it by writing like this,

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

Another way of doing this is (but inefficient)

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

Rest you are doing it right. :)

This is kind of downcasting it (loss of information can happen), so you need to be sure that the number on the right side is within the range of int only.

CodePudding user response:

Consider using class java.util.Random to generate random ints.

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

CodePudding user response:

Mixing types impacts your results

You said:

I'm expecting my code to give me a random integer from 0 to 61

Your expectation is incorrect.

First you mix types in within these parentheses: (62*Math.random()). On the left is an int, as inferred by the compiler. (See the Java Language Specification about literals.) On the right is a call to Math.random(), which is documented as returning a double. What happens when you multiply an int by a double in Java? The result is a double.

Then you pass that double value in your call to Math.round. Study the Javadoc. You will find two methods named round, each taking a different type of parameter. And each returns a different type. The method Math.round(double a) returns a long, while the other Math.round returns an int.

You would have avoided this trickery by keeping your code utterly simple rather than nesting operations within a line of code. You would incrementally declare each stage in the calculation, making you quite aware of the data type of each return value. Step-by-step results makes your debugging easier. And, as a bonus, simple code is more likely to be highly optimized by the compiler.

Also, you can call Math.toIntExact to convert a long to an int when you are sure the value will fit. This method throws an exception if the value overflows an int. So you will be alerted if your expectation of fitting proves incorrect.

Also, your code will be much easier to comprehend if you avoid magic numbers. Use well-named variables/constants instead. For example, I did not immediately understand your 62 multiplicand is the length of your pool of characters. And another point: Such a number should be soft-coded with a call to String#length.

One more problem: Your code breaks if the starting position on the pool happens to be the last character in the pool. Then adding one takes you past the end, resulting in a runtime error. So subtract one from your limit of 62, to get 61. Or in code below, ( POOL_OF_CHARACTERS.length() - 1 ).

Pulling that all together, you may get code like this:

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

When run.

randomString = 7YqRnJ4TXzMo3nvoUkBRQYq5evOhBjo3I2Cc9fTWAfCFlGY5R4rlFjWqqwRqERkVylXI7le4VLs5nKBHlNuHb03qxFeebDBSsHab

By the way, I would replace Math.random with the more convenient ThreadLocalRandom#nextInt( int origin , int bound ). Notice how that method is Half-Open, with the beginning being inclusive while the ending is exclusive. So we will not have a problem with going beyond the end of the pool. We do not need to subtract one.

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

By the way, just for fun, here is an entirely different way to accomplish the same result. This approach uses streams, and code points. (Not necessarily better though, in this case.)

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();

Or, shorter:

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();
  • Related