用不同的嵌套函数重构重复的循环代码

Refactoring repeated loop code with differing nested functions

为了图像处理目的,我有两个 for 循环遍历 2D 像素阵列。我想执行各种不同的方法,这些方法将改变对每个像素执行的功能。但是,for 循环的代码在这些方法中的每一个中都会重复。

有什么办法可以拔出来吗?

/**
 * Get pixel array from current image.
 */
public void updatePixels() {
    pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH];
    for (int row = 0; row < IMAGE_HEIGHT; row++) {
        for (int col = 0; col < IMAGE_WIDTH; col ++) {
            pixels[row][col] = image.getRGB(col, row);
        }
    }
}

/**
 * Set pixel array to current image.
 */
public void commitPixels() {
    for (int row = 0; row < IMAGE_HEIGHT; row++) {
        for (int col = 0; col < IMAGE_WIDTH; col ++) {
            image.setRGB(col, row, pixels[row][col]);
        }
    }
}

public void greyscale() {
    updatePixels();
    
    for (int row = 0; row < IMAGE_HEIGHT; row++) {
        for (int col = 0; col < IMAGE_WIDTH; col++) {
            Color currentColour = new Color(pixels[row][col]);
            int r = currentColour.getRed();
            int g = currentColour.getGreen();
            int b = currentColour.getBlue();
            
            int avg = Math.round((float)((r + g + b) / 3));
            pixels[row][col] = new Color(avg, avg, avg).getRGB();
        }
    }
    commitPixels();
}

public void mirror() {
    updatePixels();
    
    int [][] pixelCopy = pixels.clone();
    
    for (int row = 0; row < IMAGE_HEIGHT; row++) {
        for (int col = 0; col < IMAGE_WIDTH; col++) {
            pixels[row][col] = pixelCopy[row][IMAGE_WIDTH - col - 1];
        }
    }
    commitPixels();
}

从代码看来 updatePixelscommitPixel 可以移动到 greyscale

for 循环中
// updatePixel and commitPixel could be made inline as well.
public void updatePixel(int row, int col) {
    pixels[row][col] = image.getRGB(col, row);
}
    

public void commitPixel(int row, int col) {
    image.setRGB(col, row, pixels[row][col]);    
}

public void computeColor(int row, int col) {
    Color currentColour = new Color(pixels[row][col]);
    int r = currentColour.getRed();
    int g = currentColour.getGreen();
    int b = currentColour.getBlue();
                
    int avg = Math.round((float)((r + g + b) / 3));
    pixels[row][col] = new Color(avg, avg, avg).getRGB();
}
    
public void greyscale() {
    pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH];
    
    for (int row = 0; row < IMAGE_HEIGHT; row++) {
        for (int col = 0; col < IMAGE_WIDTH; col++) {
            updatePixel(row, col);
            computeColor(row, col);
            commitPixel(row, col);
        }
    }       
}

通过这种方法,您可以将 for 循环的执行次数从 3 次减少到 1 次。

由于循环是相同的,所以您可以用一种方法在 pxels 上执行所有操作

public void greyscale() {
    pixels = new int[IMAGE_HEIGHT][IMAGE_WIDTH]; // updatePixels
    for (int row = 0; row < IMAGE_HEIGHT; row++) {
        for (int col = 0; col < IMAGE_WIDTH; col++) {
            pixels[row][col] = image.getRGB(col, row); // updatePixels
            Color currentColour = new Color(pixels[row][col]);
            int r = currentColour.getRed();
            int g = currentColour.getGreen();
            int b = currentColour.getBlue();

            int avg = Math.round((float)((r + g + b) / 3));
            pixels[row][col] = new Color(avg, avg, avg).getRGB();
            image.setRGB(col, row, pixels[row][col]); // commitPixels
        }
    }
}

您可以将循环隐藏在一个方法中,该方法采用 BiConsumer 作为获取当前列和行的参数:

private void forEachPixel(BiConsumer<Integer, Integer> consumer) {
    for (int row = 0; row < pixels.length; row++) {
        for (int col = 0; col < pixels[0].length; col++) {
            consumer.accept(row, col);
        }
    }
}

现在,您可以为应该在嵌套循环内完成的操作编写 lambda:

public void updatePixels() {
    forEachPixel((row, col) -> pixels[row][col] = image.getRGB(col, row));
}

public void commitPixels() {
    forEachPixel((row, col) -> image.setRGB(col, row, pixels[row][col]));
}

public void greyscale() {
    updatePixels();
    forEachPixel((row, col) -> {
        Color currentColour = new Color(pixels[row][col]);
        int r = currentColour.getRed();
        int g = currentColour.getGreen();
        int b = currentColour.getBlue();
        int avg = Math.round(((r + g + b) / 3f));
        pixels[row][col] = new Color(avg, avg, avg).getRGB();
    });
    commitPixels();
}

public void mirror() {
    updatePixels();
    int[][] pixelCopy = Arrays.stream(pixels).map(int[]::clone).toArray(int[][]::new);
    forEachPixel((row, col) -> pixels[row][col] = pixelCopy[row][pixels[0].length - col - 1]);
    commitPixels();
}

备注:在您的问题中,您在 mirror() 内的二维数组上使用 .clone()。这会创建一个浅克隆,但您需要为后面的逻辑进行深克隆。这个问题在我的回答中得到解决。在 greyscale() 方法中,除法后不要强制转换为浮点数,而是将除数设为浮点数(参见我的回答)。