Java 8流 - 修改组中的所有元素

5

我有一个Class A对象的集合

class A {
    String code;
    long timestamp;
    long largestTimestamp;
}

我需要为每个对象填充largestTimestamp字段(相同代码组中具有最大“timestamp”值的对象)。我可以按照以下两个步骤完成此操作 -

Map<String, Long> largestTimestampMap = list.stream().collect(Collectors.toMap(A::getCode, A::getTimestamp, Long::max));
list.forEach(a -> a.setLargestTimestamp(largestTimestampMap.get(a.getCode())));

有没有一种方法将它们合并成一个流链?

你可以使用 list.replaceAll(a -> {a.setLargestTimestamp(largestTimestampMap.get(a.getCode()));return a;}); 替代 list.forEach(...) - Hadi J
3
如答案所示:有一种方法可以将它们结合起来。而从答案中明显可以看出:您不应该这样做。这是两个不同的步骤,请保持它们的分离以提高可维护性和可读性。签名:未来需要维护您代码的程序员 - Marco13
3个回答

4
可以将它们合并为单个 pipeline,具体步骤如下:
list.stream()
     .map(a -> new A(a.getCode(), a.getTimestamp(),
                list.stream()
                    .filter(b -> a.getCode().equals(b.getCode()))
                    .mapToLong(A::getTimestamp)
                    .max().getAsLong()))           
     .collect(Collectors.toList());

如果您想避免创建新列表,而是像您的示例中那样修改现有列表,则请使用replaceAll

list.replaceAll(a -> new A(a.getCode(), a.getTimestamp(),
                        list.stream()
                                .filter(b -> a.getCode().equals(b.getCode()))
                                .mapToLong(A::getTimestamp)
                                .max().getAsLong()));

然而,我建议避免使用这种方法:

1)因为它的性能比你当前的实现差,这种解决方案需要在list上再次迭代每个元素,而你展示的 Map 方法只需要一个单独的 get 调用,我们都知道查找 Map 的速度是多么快。

2)代码更多。

是的,你可以将 map 中间操作内部的代码重构成方法,使其看起来更短,但最终它仍然是更长的。

有时候最好的方法需要两个或更多独立的行,在这种情况下,最好继续使用你的方法。


replaceAll 解决方案中,使用 setLargestTimestamp(...) 而不是创建 对象会更好吗?list.replaceAll(a -> {a.setLargestTimestamp(largestTimestampMap.get(a.getCode()));return a;}); - Hadi J
1
如果要使用largestTimestampMap.get(a.getCode()),那么我建议仍然使用forEach,例如list.forEach(a -> a.setLargestTimestamp(largestTimestampMap.get(a.getCode())));,因为这更符合惯用语和直觉。如果要使用我上面提到的replaceAll解决方案(我不建议这样做),那么是的,你可以使用带有lambda语句块的replaceAll来设置对象的属性并返回它,这意味着不一定需要像上面展示的那样创建新的A对象。顺便说一下,好主意! - Ousmane D.

3
你可能只是在寻找(如果“为每个对象填充largestTimestamp字段”意味着为整个列表保存相同的largestTimeStamp):
// find the largest timestamp
long largestTimeStamp = list.stream()
        .mapToLong(A::getTimestamp)
        .max()
        .orElse(Long.MIN_VALUE);

// iterate through the list to set its value
list.forEach(a -> a.setLargestTimestamp(largestTimeStamp));

如果必须在一行代码中完成,您可以使用以下代码(已根据Aomine在评论中的澄清进行了编辑):

List<A> output = list.stream()
        .map(a -> new A(a.getCode(), a.getTimestamp(),
                list.stream().filter(b -> a.getCode().equals(b.getCode()))
                        .mapToLong(A::getTimestamp).max().orElse(a.getTimestamp())))
        .collect(Collectors.toList());

1
接近了但不完全正确。这个代码块获取largestTimeStamp并将"每一个"元素的时间戳设置为该值,而不是原始代码所做的那样。原始代码获取给定 getCode 的最大时间戳,并将该特定对象设置为检索到的值。请参阅我的流式处理方法以获得更多说明 - Ousmane D.
@Aomine 我现在明白你的意思了... 在问题“为每个对象填充largestTimestamp字段”中,我理解为为所有对象设置相同的值,而代码确实保留了列表中具有相同代码的对象的最大值.. 确实需要一个过滤器。 - Naman

0

只是另一种想法,但你不能将A类结构化为下面所示吗?

class A {
  String code;
  long timeStamp;
  TimeStampWrapper timeStampWrapper;
}

class TimeStampWrapper {
  long maxTimeStamp;
  long minTimeStamp;
  ...
}

采用这种方法,您只需要进行1次流遍历,并执行一次设置操作即可设置maxTimeStamp、minTimeStamp等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接