Java Lambda - 更好地查找包含Vector3D实例的三角形的极值

时间:2018-06-02 18:00:11

标签: java vector lambda compare java-stream

三角形多边形的数组vertices包含3x Vector3D个实例。方法应该在三角形的轴(x,y或z)上找到极值(最小值/最大值)。

E.g。三角形A(0, 0, 3) B(1, 2, 3) C(2, 2, -4)在z轴上的最小值为-4

目前的实现如下:

   public class Triangle implements Iterable<Vector3D> {
        private final Vector3D[] vertices;
        private final double kEpsilon = 1e-8;

        public Triangle(Vector3D... vertices) {
            Preconditions.checkArgument(vertices.length == 3);
            this.vertices = vertices;
        }

        public double getExtremeValueForAxis(int axis) {
            var minimum = getValueByAxisIndex(Arrays.stream(vertices).min((v, vOther) -> {
                var vValueOnAxis = getValueByAxisIndex(v, axis);
                var vOtherValueOnAxis = getValueByAxisIndex(vOther, axis);
                return Double.compare(vValueOnAxis, vOtherValueOnAxis);
            }).get(), axis);
            return minimum;
        }

        private static double getValueByAxisIndex(final Vector3D vertex, final int axis) {
            switch(axis) {
                case 0: return vertex.getX();
                case 1: return vertex.getY();
                case 2: return vertex.getZ();
                default: throw new IllegalArgumentException("Axis needs to be in 3D space!");
            }
        }
  }

如何通过传递函数Optional<T> min(Comparator<? super T> comparator);Optional<T> max(Comparator<? super T> comparator);来启用用户搜索最小/最大值以避免布尔标志(例如boolean searchForMin)以及如何减少我实施的整体复杂性?

2 个答案:

答案 0 :(得分:1)

我会先使用mapToInt(ToIntFunction<? super T> mapper)将流中的项目映射到int,然后使用方法min()获取最小值。此方法不需要参数,因为它与IntStream一起使用,而直接比较整数。

int minX = Arrays.stream(vertices).mapToInt(Vector3D::getX).min().getAsInt();
int minY = Arrays.stream(vertices).mapToInt(Vector3D::getY).min().getAsInt();
int minZ = Arrays.stream(vertices).mapToInt(Vector3D::getZ).min().getAsInt();

int min =  Arrays.asList(minX, minY, minZ).stream().mapToInt(i -> i).min().getAsInt();

或者,将所有值连接到一个List中并对其执行Stream操作:

List<Integer> integers = new ArrayList<>();
Arrays.stream(vertices).forEach(i -> { 
    integers.add(i.getX()); integers.add(i.getY()); integers.add(i.getZ());
});
int min = integers.stream().mapToInt(i -> i).min().getAsInt();

答案 1 :(得分:1)

通过让调用者同时选择比较器(在本例中为reducer)和使用函数的轴,可以真正简化函数及其调用:

/** valueFunction selects the axis 
    reducer selects which of 2 values to retain (max/min in this case)*/
public double getExtremeValueForAxis(
   Function<Vector3D, Double> valueFunction, BinaryOperator<Double> reducer) {
     return Arrays.stream(vertices)
              .map(valueFunction)
              .reduce(reducer)
              .get();
}

然后可以简单地将其称为:

new Triangle().getExtremeValueForAxis(Vector3D::getX, Math::max); //max by axis 0
new Triangle().getExtremeValueForAxis(Vector3D::getY, Math::min); //min by axis 1

当然,也可以使用lambda表达式提供内联reducer和mappers:

new Triangle().getExtremeValueForAxis(v -> v.getY(), 
            (n1, n2) -> n1 > n2 ? n2 : n1); //min by axis 0