我正在使用Java 8并尝试编写纯功能代码。在我的最新项目中,我需要逐步浏览图像中的每个像素,并对每个像素执行一些计算。我提出的代码是这样的:
IntStream
.range(0, newImage.getWidth())
.forEach(i -> IntStream
.range(0, newImage.getHeight())
.forEach(n -> {
inspectPixel(i, n, newImage);
})
);
然而,命令式版本是这样的:
for (int i = 0; i < newImage.getWidth(); i++){
for (int n = 0; n < newImage.getHeight(); n++){
inspectPixel(i, n, newImage);
}
}
也许这只是因为我太习惯于命令式编程,但后者似乎比前者更具可读性。其中一件事情正在发生:
答案 0 :(得分:1)
那是因为IntStream
并非专门针对迭代像素。冗长是对流的力量的牺牲。虽然功能齐全,但它的目的并不符合您的需求。
你总是可以创建自己的界面来处理凌乱的工作:
class PixelScanner {
public static void scan(BufferedImage image, PixelInspector inspector) {
int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < image.getWidth(); x++) {
int pixel = pixels[x + y * image.getWidth()];
inspector.inspect(pixel);
}
}
}
}
interface PixelInspector {
void inspect(int pixel);
}
然后您可以将其用作:
PixelScanner.scan(image, pixel -> {
// inspect pixel
});
甚至可以添加更多参数进行检查,例如像素所在的(x,y)位置。您甚至可以包装每个像素以将更多关于它的数据传递给检查器。我还建议使scan
非静态并使用PixelScanner
对象。功能编程很有用,但OOP肯定有它的聚光灯时刻,两者都应该有效使用。
答案 1 :(得分:0)
你的代码是错误的,错误的我的意思是根本错误,而不是风格。但是,这是编码风格不好的结果。你应该给变量有意义的名字。 i
和n
等名称不适合实际持有x
和y
坐标的变量。如果你给他们命名为x
和y
,你会立即注意到这个问题,我想:
IntStream
.range(0, newImage.getWidth())
.forEach(x -> IntStream
.range(x, newImage.getHeight())
.forEach(y -> {
inspectPixel(x, y, newImage);
})
);
显然,range(x, newImage.getHeight())
不能正确......
也就是说,嵌套forEach
调用确实通常是对命令式代码进行转换的标志,如果找不到更好的解决方案,那么这些代码很可能仍然是必要的。
由于您希望单独处理像素,您可以使用flatMap
生成坐标流,但是您需要一种类型来将这些作为结果流的元素保存,例如
IntStream.range(0, newImage.getWidth()).boxed()
.flatMap(x -> IntStream.range(0, newImage.getHeight()).mapToObj(y -> new Point(x, y)))
.forEach(point -> inspectPixel(point.x, point.y, newImage));
这里,我们在点实例中保持坐标。不幸的是,我们必须在此处设置x
坐标,因为IntStream
不提供flatMapToObj
操作。
如果对象创建困扰您,您可以使用压缩long
替换点实例,这也允许将x
值作为原始数据类型进行处理,但当然,它不会不会增加可读性:
LongStream.range(0, newImage.getWidth())
.flatMap(x -> IntStream.range(0, newImage.getHeight()).mapToLong(y -> (long)x<<32|y))
.forEach(point -> inspectPixel((int)(point>>>32), (int)point, newImage));
当然,如果您只对像素数据感兴趣并且x
和y
值只是为了帮助您访问它们,那么您可以首先在像素上流式传输像素:
Arrays.stream(newImage.getRGB(0, 0, newImage.getWidth(), newImage.getHeight(),
null, 0, newImage.getWidth()))
.forEach(argb -> inspectPixel(argb)));