Java 8+流:检查列表的对象实例的两个字段的顺序是否正确

Kev*_*sen 17 java sorting boolean-expression java-8 java-stream

标题可能有点模糊,但这是我所拥有的(在私有化代码中):

包含一些字段的类,包括BigDecimal和Date:

class MyObj{
  private java.math.BigDecimal percentage;
  private java.util.Date date;
  // Some more irrelevant fields

  // Getters and Setters
}
Run Code Online (Sandbox Code Playgroud)

在另一个类中,我有一个这些对象的列表(即java.util.List<MyObj> myList).我现在想要的是一个Java 8流来检查列表是否符合我的验证器的日期和百分比的正确顺序.

例如,以下列表将是真实的:

[ MyObj { percentage = 25, date = 01-01-2018 },
  MyObj { percentage = 50, date = 01-02-2018 },
  MyObj { percentage = 100, date = 15-04-2019 } ]
Run Code Online (Sandbox Code Playgroud)

但是这个列表是假的,因为百分比的顺序不正确:

[ MyObj { percentage = 25, date = 01-01-2018 },
  MyObj { percentage = 20, date = 01-02-2018 },
  MyObj { percentage = 100, date = 15-04-2019 } ]
Run Code Online (Sandbox Code Playgroud)

此列表也将是假的,因为日期的顺序不正确:

[ MyObj { percentage = 25, date = 10-03-2018 },
  MyObj { percentage = 50, date = 01-02-2018 },
  MyObj { percentage = 100, date = 15-04-2019 } ]
Run Code Online (Sandbox Code Playgroud)

一种可能的解决方案可能是创建Pairs 这样的,然后使用!.anyMatch检查每个人Pair<MyObj>.但是Pair如果可能的话,我真的不想为此目的创建一个类.

是否有一种方法可以使用.reduce或某些东西循环对MyObj检查它们?这里最好的方法MyObj是使用Java 8流检查列表中所有日期和百分比是否按正确顺序排列?

另一种可能性是按日期排序列表,然后检查它们是否全部按百分比排序,如果这比检查两个字段是同一时间更容易.但是,MyObj仍然存在比较百分比对的相同问题.

(PS:我将它用于a com.vaadin.server.SerializablePredicate<MyObj> validator,我更喜欢Java 8 lambda,因为我还使用了一些用于其他验证器,所以它更符合其余的代码.Java 8 lambda更像是然而,偏好比我的问题中的要求.)

Eug*_*ene 17

好吧,如果你想要一个短路操作,我不认为使用stream-api的简单解决方案......我建议一个更简单的方法,首先定义一个方法,以短路的方式告诉你你的List是是否排序,基于一些参数:

 private static <T, R extends Comparable<? super R>> boolean isSorted(List<T> list, Function<T, R> f) {
    Comparator<T> comp = Comparator.comparing(f);
    for (int i = 0; i < list.size() - 1; ++i) {
        T left = list.get(i);
        T right = list.get(i + 1);
        if (comp.compare(left, right) >= 0) {
            return false;
        }
    }

    return true;
}
Run Code Online (Sandbox Code Playgroud)

并通过以下方式调用:

 System.out.println(
          isSorted(myList, MyObj::getPercentage) && 
          isSorted(myList, MyObj::getDate));
Run Code Online (Sandbox Code Playgroud)

  • 实际上,它应该只是`if(comp.compare(left,right)> 0)返回false;`,因为相等的元素不会违反sorted属性.返回`Supplier <Boolean>`而不是`boolean`的目的是什么? (3认同)
  • 我被OP的比较器示例误导了.实际上,`isSorted(...)&& isSorted(...)`是有意的.我认为没有理由在这里使用"供应商".或者,你可以接受一个`BiPredicate`而不是`Comparator`,让调用者提供一个检查这两个属性的函数. (2认同)
  • 在这种情况下,使用"Supplier <Boolean>"是没用的,因为您评估整个列表以创建该供应商,并且评估供应商基本上是免费的(返回常量值).它只是使一切都不那么可读.我不认为应该保留部分答案. (2认同)

LuC*_*Cio 5

我认为您几乎可以通过尝试使用Stream.anyMatch. 你可以像这样完成它:

private static boolean isNotOrdered(List<MyObj> myList) {
    return IntStream.range(1, myList.size()).anyMatch(i -> isNotOrdered(myList.get(i - 1), myList.get(i)));

}

private static boolean isNotOrdered(MyObj before, MyObj after) {
    return before.getPercentage().compareTo(after.getPercentage()) > 0 ||
            before.getDate().compareTo(after.getDate()) > 0;
}
Run Code Online (Sandbox Code Playgroud)

我们可以使用IntStream.range索引来迭代列表的元素。这样我们就可以引用列表中的任何元素,例如前一个元素来比较它。

编辑添加一个更通用的版本:

private static boolean isNotOrderedAccordingTo(List<MyObj> myList, BiPredicate<MyObj, MyObj> predicate) {
    return IntStream.range(1, myList.size()).anyMatch(i-> predicate.test(myList.get(i - 1), myList.get(i)));
}
Run Code Online (Sandbox Code Playgroud)

这可以使用上述谓词调用如下:

isNotOrderedAccordingTo(myList1, (before, after) -> isNotOrdered(before, after));
Run Code Online (Sandbox Code Playgroud)

或者在类中使用方法引用ListNotOrdered

isNotOrderedAccordingTo(myList1, ListNotOrdered::isNotOrdered)
Run Code Online (Sandbox Code Playgroud)