当前位置: 首页 > 知识库问答 >
问题:

Java8流中的条件排序[重复]

闽高峯
2023-03-14

我正在尝试根据买入或卖出方向对股票订单列表进行排序。

我试过这样的方法:

orders.stream()
      .sorted(o -> "BUY".equals(o.side) ? 
              comparing(Order::getPrice) : 
              comparing(Order::getPrice).reversed());

我看到下面的错误消息,我不清楚。

不兼容的类型。必需的int,但已将“comparing”推断给Comparator:不存在类型变量T,U的实例,因此Comparator符合Integer。

共有3个答案

赵永新
2023-03-14

这里有一些东西可以根据属性对列表进行排序以比较规则,同时仍然保持该属性的原始顺序。

假设我们有一个order

class Order {
    private String side;
    private int price;
    // Getters and setters omitted for brevity
}

还假设我们有一个订单列表:

BUY  4
BUY  5
SELL 1
BUY  8
SELL 6
BUY  3
SELL 9
SELL 2
BUY  7

以下代码将按升序排列购买订单,降序排列销售订单,但保留原始列表中的购买和销售顺序。在本例中,第一个条目是购买订单,第二个也是购买订单,第三个是销售订单,等等。结果是:

BUY  3
BUY  4
SELL 9
BUY  5
SELL 6
BUY  7
SELL 2
SELL 1
BUY  8

代码如下:

// We define a comparator here which returns ascending order if the side is BUY,
// or descending if it is SELL.
final Comparator<Order> comparator = (left, right) -> {
    Comparator<Order> c = Comparator.comparing(Order::getPrice);
    if (Objects.equals(left.getSide(), "SELL")) {
        c = c.reversed();
    }
    return c.compare(left, right);
};

// We stream over the orders, and partition them by their side. We sort the
// resulting lists by their own comparison method.
Map<String, List<Order>> map = orders.stream()
    .collect(Collectors.groupingBy(Order::getSide, toSortedList(comparator)));
Iterator<Order> buyIt = map.get("BUY").iterator();
Iterator<Order> sellIt = map.get("SELL").iterator();

// At last, we stream again over the elements, consuming from both iterators
// based on the value of 'side'
orders.stream()
    .map(order -> Objects.equals(order.getSide(), "BUY") ? buyIt.next() : sellIt.next())
    .forEach(System.out::println);
// Almost the same as Collectors.toList(), but sorts the list by the provided
// comparator.
public static <T> Collector<T, List<T>, List<T>> toSortedList(Comparator<T> comparator) {
    return Collector.of(
        ArrayList::new,
        List::add,
        (left, right) -> {
            left.addAll(right); return left;
        },
        list -> list.stream()
            .sorted(comparator)
            .collect(Collectors.toList())
    );
}
仲元凯
2023-03-14

我有另一个想法:如何根据类型对流进行分区,然后对排序顺序进行后期修复?

Map<Boolean, List<Order>> filtered = orders.stream()
                   .sorted(Order::getPrice)
                   .collect(Collectors.partitioningBy(o -> "BUY".equals(o.side)));
// #partitioningBy guarantees true/false will be in the map
List<Order> bought = filtered.get(true);
List<Order> sold = filtered.get(false);
Collections.reverse(sold); //since you want sold backwards

缺点是对于大订单(数百件商品),这可能会很慢,因为您同时进行买卖(而不是两种较小的订单)。您可以将排序移到下游收集器(甚至是收集的对象)中,但它需要更多的代码

Map<Boolean, List<Order>> filtered = orders.stream()
                   .collect(Collectors.partitioningBy(o -> "BUY".equals(o.side)));
List<Order> bought = filtered.get(true);
List<Order> sold = filtered.get(false);
bought.sort(Comparator.comparingInt(Order::getPrice));
sold.sort(Comparator.comparingInt(Order::getPrice).reversed());

对于较小的输入,可以接受第一个输入。最重要的是,它消除了(有些)凌乱的lambda,消除了三元/lambda混合。

编辑:

如果您只对其中一个结果感兴趣,那么没有必要让逻辑在同一个流中:

//To fetch the items that were not "BUY"
List<Order> sold = orders.stream()
                .filter(o -> !"BUY".equals(o.side))
                .sorted(Comparator.comparingInt(Order::getPrice).reversed())
                .collect(Collectors.toList());

//or if you're looking for the sold items at this point in code
List<Order> bought = orders.stream()
                .filter(o -> "BUY".equals(o.side))
                .sorted(Comparator.comparingInt(Order::getPrice))
                .collect(Collectors.toList());
裴彦
2023-03-14

您可能需要使用这个比较器

static Comparator<Order> buyComparator() {
    return (l, r) -> {
        Comparator<Order> comparator = Comparator.comparing(Order::getPrice);
        comparator = "BUY".equals(l.side) && "BUY".equals(r.side) ?
                comparator : comparator.reversed();
        return comparator.compare(l ,r);
    };
}

这样,用法就相当简单了,您可以通过流API返回一个新的排序列表,或对原始列表进行修改:

  1. 流API:
List<Order> sortedOrders = orders.stream()
         .sorted(buyComparator())
         .collect(Collectors.toList());
Collections.sort(orders, buyComparator());

顺便说一下,您的代码中有两个错误:

  1. 没有流::排序方法,但流::排序。
  2. Stream本身不会终止,因此管道永远不会执行。此类终端操作是收集减少findFirst等...

...如果我只使用排序(比较(Order::getPrice))而不是三元操作,为什么它不抱怨呢?

因为排序的方法需要比较器

首先,lambda表达式以o开头-

其次,返回类型必须是int(因为int比较(T o1, T o2))。仅使用比较(order::getPrice)是可以的,但只要您选择lambda表达式而不是方法引用,那么返回类型就会清晰可见:

// this is your lambda expression
BiFunction<Order, Order, Comparator<Order>> biFunction = (l, r) -> 
    "BUY".equals(l.side) && "BUY".equals(r.side) ?
            Comparator.comparing(Order::getPrice) :
            Comparator.comparing(Order::getPrice).reversed();

List<Order> sortedOrders = orders.stream()
            .sorted((l, r) -> biFunction.apply(l, r).compare(l ,r))
            .collect(Collectors.toList());

这显然不是比较器

 类似资料:
  • 哦,那些狡猾的Java8条带有lambdas的溪流。它们非常强大,但是复杂的东西需要一点时间来包裹它。 假设我有一个类型,其属性为。假设我有这些用户的地图 ?

  • 问题内容: 我有3个字段的“任务”表: 日期 优先级(0,1,2) 完成(0,1) 我想要实现的是对整个表按“完成”标志排序,未完成的任务应按优先级排序,而已完成的任务应按日期排序: 从按完成的asc的任务顺序中选择* 如果完成= 0,则按优先级依次排序 如果完成= 1,则另外按日期顺序排序 不使用工会就可以在MySQL中做到这一点吗? 谢谢。 问题答案: 您可以尝试在使用aux来计算aux时根据

  • 我想显示一个名为listaBaresFiltrada的列表,按多个值排序。 Evento模型: 现在,我可以创建第一个排序条件,如下所示: 但我不知道如何包括其他两个排序条件。

  • 我有一个的列表 其中是一个模型类,如下所示 null

  • 我有一个列表,像

  • 寻找有关以下用例的建议或解决方案 应用程序接收按功能键(如员工id)标识的更改时间排序的消息。功能键可以有多条消息 每条消息都会触发一个工作流。如果员工有待定工作流,则希望将新消息排队,直到待定工作流完成 是否有任何方法可以在节奏中对消息重新排序,以将它们作为由消息中的功能键标识的组进行处理?