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

在java对象中创建12个月的滚动数据[已关闭]

岳茂
2023-03-14

我有一个java对象,比如映射

此对象包含以下格式的数据:

202103, 2
202104, 4
202105, 0
202106, 9
.
.
.
.
202202, 10

第一个值是月份键,第二个值是案例计数。

我需要每月创建一个翻转数据或逐月累积的案例计数。

结果应该是-

202103, 2
202104, 6
202105, 6
202106, 15
.
.
.
.
202202, 98

我们如何在使用Java流时做到这一点。


共有3个答案

屈健柏
2023-03-14

首先,您需要对TreeMap中的条目进行排序。

然后想不惜一切代价用流来解决这个问题,用iterate()方法在for循环的条目集上累积你可能模拟的迭代值。

已排序映射的第一个条目用作种子。然后,对于流中创建的每个连续条目,较高条目的键将用作新条目的键,并用作较高条目的值与流中以前条目的值之和的值。该过程将继续,直到存在更高的条目,即getAccumeratedEntry()返回的结果不是null。此条件由谓词Objects::nonNull表示。

public static void main(String[] args) {
    Map<Integer, Long> monthCaseCount =
            Map.of(202103, 2L,
                    202104, 4L,
                    202105, 0L,
                    202106, 9L);

    NavigableMap<Integer, Long> monthCaseCountSorted = new TreeMap<>(monthCaseCount);

    Map<Integer, Long> monthCaseCountAccumulated =
            Stream.iterate(monthCaseCountSorted.firstEntry(),
                            Objects::nonNull,
                           entry -> getAccumulatedEntry(monthCaseCountSorted, entry))
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

    monthCaseCountAccumulated.entrySet().stream() // this part was added only for demonstration purpose
            .sorted(Map.Entry.comparingByKey())
            .forEach(entry -> System.out.printf("%d : %d%n", entry.getKey(), entry.getValue()));
}

private static Map.Entry<Integer, Long> getAccumulatedEntry(NavigableMap<Integer, Long> monthCaseCountSorted,
                                                            Map.Entry<Integer, Long> entry) {
    Integer higher = monthCaseCountSorted.higherKey(entry.getKey());

    return higher == null ?
            null : Map.entry(higher, entry.getValue() + monthCaseCountSorted.get(higher));
}

输出

202103 : 2
202104 : 6
202105 : 6
202106 : 15

注意:更有效的方法是调整monthcasecontsorted映射中的值。而流不适合这样的任务。函数不得产生副作用,且函数的每次执行都必须具有相同的结果(如果您试图通过直接更改monthCaseCountSorted中的值来实现基于流的解决方案,则这两个条件都将被违反)

韩玉石
2023-03-14

另一种选择是使用AtomicLong存储总和,并将累积值收集到新映射中:

Map<Integer, Long> monthCaseCountMap = Map.of(  202103, 2L,
                                                202104, 4L,
                                                202105, 0L,
                                                202106, 9L);

AtomicLong sum = new AtomicLong(0L);

Map<Integer, Long> accumulated = 
        monthCaseCountMap.entrySet()
                .stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors.toMap(Map.Entry::getKey, e -> sum.addAndGet(e.getValue())));
柯默
2023-03-14

你用错工具了。当您需要执行的操作是[A]仅对流中的单个元素执行的操作,或者[B]对所有元素执行的操作时,Java streams是合适的工具。

您需要对其中一些(特别是“前辈”)进行操作。从根本上说,Stream并不是为此而设计的。你用错工具了。流具有。parallel()例如,从这个角度来看,“我面前所有东西的总和”的概念已经没有意义了。

你可以通过涉及排序的概念来重新定义这个问题(“我想要这个键的所有值和在这个键之前排序的所有值的总和”),但这将是非常低效的。

当你试图将一个工具用于它不是为之设计的东西时,这是可以预料的,流版本要么要慢得多,要么令人讨厌(混合流和非流概念,因此两者都有缺点,也都没有优点),而且很可能会有更多的代码和更复杂的代码。

只需使用迭代过程——您定义的操作在迭代思维下要简单得多。如果你想改进你的代码,请记住:你应该使用最具体的类型来准确描述你所拥有的Integer没有太多说明,LocalDate更准确,因为地图中的键显然是日期,或者至少是月-年。

public class Hospital {
  SortedMap<LocalDate, Long> caseCounts = createCaseCountMap();
// createCaseCountMap ends up doing something like:
//    caseCounts.put(LocalDate.of(2021, 03, 1), 2);
//    caseCounts.put(LocalDate.of(2021, 04, 1), 4);
//    .... etc

// option 1:

  public long getSumUntil(LocalDate mark) {
    return caseCounts.headMap(mark, true).values().stream()
      .mapToLong(Long::longValue).sum();
  }

// option 2:

  public SortedMap<LocalDate, Long> makeIncrementalMap() {
    var out = new TreeMap<LocalDate, Long>();
    long sum = 0L;
    for (var entry : caseCounts.entrySet()) {
      sum += entry.getValue();
      out.put(entry.getKey(), sum);
    }
    return out;
  }
}

在这里,对于选项2,使用流是一个非常愚蠢的想法,因为操作基本上依赖于它的一些但不是所有的邻居,而流不是为这些邻居准备的。

 类似资料:
  • 这个问题似乎与帮助中心定义的范围内的编程无关。 我只是想使它的对象,并在控制台屏幕调用方法,但它是显示静态引用的错误。 请帮助我创建一个没有此静态引用错误的对象。

  • 我正在努力实现以下布局。如果我选择curdate(),它是六月,我应该得到之前的6个月。6个月也应该包括六月。如果curdate=2018年6月25日至2018年1月,我的外出时间应该是从2018年6月至2018年1月的几个月。有谁能帮我解释一下语法吗。我现在有的只是个别的查询,这将给我在MySQL之前的6个月。 月份前几个月6月5月4月3月2月5月4月3月2月

  • 我需要创建一个对象(银行),其中包含一组客户端和bankID。我的问题是,我不知道如何在主函数中创建银行。 银行类别: 客户端类: 主要类别: 这些是问题所在: 你必须创建一个程序来模拟银行活动。该系统包括以下模块:银行—客户(客户数组)— idBank(字符串)5 BancAccount — accountNumber(字符串)—金额(浮点)客户—姓名(字符串)—地址(字符串)—账户(银行账户数

  • 问题内容: 输出为: 1988年 1月25 日星期三00:00:08 IST 1989 我过去了,但我得到了。为什么? 问题答案: 日历中的月份从零开始。因此,将12解释为12月+ 1个月。用

  • 输出为: 1988 1989年1月25 00:00:08日星期三 我通过了1988年12月25日的考试,但我得到了1989年1月25日的考试。为什么?

  • 我试图创建一个由子类定义的对象数组(我认为这是正确的术语)。我可以看到这个问题反复出现,但实现仍然存在问题。 我的代码 给出错误 线程“main”java.lang.NullPointerException中出现异常。 为了使其合理化,我将其分解为最简单的术语: 这似乎奏效了。我只是看不出我的两个例子有什么不同。我知道我的第一个没有意义,但是MyClass最终会包含更多的数据。) 我很肯定这个问题