generate a 2d array that could be used to create an image with Java This Python code
im_data = np.ones((100,100))
im_data[20:50,20:50]=np.zeros((30,30))

can generate a 2d array that could be used to create an image
plt.axis('off')
axi = plt.imshow(im_data, cmap='Greys')


I'm trying to do the same job with Java
class Arr100{
    public static void main(String[] args){
        System.out.println(&quot;H&quot;);
        int[][] arr = new int[100][100];
        for(int i=0; i&lt;arr.length; i++){
            for (int j=0; j&lt;arr[i].length; j++){
                arr[i][j]=1;
            }
        }
        for(int i=20; i&lt;50; i++){
            for (int j=20; j&lt;50; j++){
                arr[i][j]=0;
            }
        }
    }
}

In terms of GPU and memory, is there a better way to do the same thing?
You don't need to manually manage nested for loops.
int[][] pixels = new int[100][100];
for (int[] row : pixels) {
    Arrays.fill(row, 1);
}

for (int i = 20; i &lt; 50; i++) {
    Arrays.fill(pixels[i], 20, 50, 0);
}

This will make better use of optimizations available for managing array values.
I personally don't like arr as a name for an array.  If I need a generic name, I tend to use data.  I used pixels here.  Or colors would work.
This may be more or less efficient than
int[][] pixels = new int[100][100];
int i = 0;
for (; i &lt; 20; i++) {
    Arrays.fill(pixels[i], 1);
}
for (; i &lt; 50; i++) {
    Arrays.fill(pixels[i], 0, 20, 1);
    Arrays.fill(pixels[i], 50, pixels[i].length, 1);
}
for (; i &lt; pixels.length; i++) {
    Arrays.fill(pixels[i], 1);
}

That doesn't set any of the pixels twice and relies on the default values for the 0 pixels.  That may be more efficient.  Although it is also possible that it is easier to set the entire array to one value than to work with rows and parts of rows.
Note that if you are willing to change the representation, then
int[][] pixels = new int[100][100];
for (int i = 20; i &lt; 50; i++) {
    Arrays.fill(pixels[i], 20, 50, 1);
}

would probably be better than either.  In this, black is the default 0 while 1 is the white square.
The third form is the least code and probably fastest.  The only way that I could see it being as slow or slower than either of the others is if something is happening implicitly.  For example, if there is a memory initialization that the first form skips because it immediately does its own initialization.
The first form is less code than the second.  The comparative speed would probably depend on the platform (and possibly compiler).  Platforms that allow for managing memory as blocks would probably find the first quicker, while platforms that can only address one word at a time would probably find the second quicker.
You would have to do timing tests on all platforms where you expect to run to get a real idea of the speed.
There is probably a streams-based version that is faster as well.
If you want to go further and try to pass the information for regions, you would either have to write your own solution or use a third party library.  Note that such a thing would probably be slower.  But it might be less code.  I suspect that your Python example is also slower on some platforms (albeit less code).  My guess is that Python implements it much as the first example code block in this answer.