Use OpenSimplex noise: https://gist.github.com/KdotJPG/b1270127455a94ac5d19
Do sommething like:
protected void generateLevel() {
OpenSimplexNoise noise = new OpenSimplexNoise(); //optionally pass in a seed.
for (int y = 0 y < height; y++) {
for (int x = 0; x < width; x++) {
double value = noise.eval(x / 24.0, y / 24.0, 0.5);
if (value < 0) {
tiles[x + y * width] = 6; //set water
} else {
tiles[x + y * width] = 1; //set grass
}
}
}
}
There are also alternatives such as Perlin noise and Simplex noise. I'd skip Perlin noise entirely because it tends to exhibit visually significant grid artifacts (it lines all of its features up with its underlying square grid). Simplex noise in 2D is okay, though most implementations of 2D Simplex noise on the internet use a weird gradient set that results in artifacts down the negative diagonal. And 3D simplex noise is patent-saddled.
Perlin noise vs OpenSimplex noise, 2D slices of 3D:

- Left is noise(x, y, 0) grayscale
- Next is noise(x, y, 0) > 0 ? white : black
- Next is |noise(x, y, 0)| > 0.1 ? white : black
- Next is noise(x, y, 0.5) grayscale
EDIT: Here's the code from the comment
Fractal noise:
OpenSimplexNoise n1 = new OpenSimplexNoise(seed1);
OpenSimplexNoise n2 = new OpenSimplexNoise(seed2);
OpenSimplexNoise n3 = new OpenSimplexNoise(seed3);
for each x,y {
double value = (n1.eval(x / 48.0, y / 48.0, 0.5) + n2.eval(x / 24.0, y / 24.0, 0.5) * .5 + n3.eval(x / 12.0, y / 12.0, 0.5) * .25) / (1 + .5 + .25);
//Do whatever you need to with that value
}
Rivers:
if (value > -0.1 || value < 0.1)
water
else
land
Biomes:
OpenSimplexNoise n1 = new OpenSimplexNoise(seed1);
OpenSimplexNoise n2 = new OpenSimplexNoise(seed2);
OpenSimplexNoise n3 = new OpenSimplexNoise(seed3);
for each x,y {
double elevation = n1.eval(x / 24.0, y / 24.0, 0.5);
double temperature = n2.eval(x / 24.0, y / 24.0, 0.5);
double precipitation = n3.eval(x / 24.0, y / 24.0, 0.5);
if (elevation < 0)
water
else {
//decide biome based on temperature, precipitation, and elevation
}
}