Java8: 修改forEach循环外部变量的引用

huangapple go评论64阅读模式
英文:

Java8: change the reference of the variable outside of forEach

问题

我尝试同时做两件事情:

1. 对列表中对象的特定字段进行求和

AtomicReference<BigDecimal> doReqQtySum = new AtomicReference<>(BigDecimal.ZERO);
AtomicReference<BigDecimal> boQtySum = new AtomicReference<>(BigDecimal.ZERO);
models.forEach(detail -> {
    doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -> bg1.add(bg2));
    boQtySum.accumulateAndGet(detail.getBoQty(), (bg1, bg2) -> bg1.add(bg2));
});

2. 从列表中过滤出一个对象

DoRequestDetailModel originProductRequestDetail = models.stream()
    .filter(m -> m.getIsOriginProduct())
    .reduce((a, b) -> {
         throw new IllegalStateException();
    })
    .get();

我希望这段代码能够工作,但实际上它并不能:

AtomicReference<BigDecimal> doReqQtySum = new AtomicReference<>(BigDecimal.ZERO);
AtomicReference<BigDecimal> boQtySum = new AtomicReference<>(BigDecimal.ZERO);
DoRequestDetailModel originProductRequestDetail = new DoRequestDetailModel();
models.forEach(detail -> {
     doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -> bg1.add(bg2));
     boQtySum.accumulateAndGet(detail.getBoQty(), (bg1, bg2) -> bg1.add(bg2));
     if(detail.getIsOriginProduct()) {
          originProductRequestDetail = detail;
     }
});

下面的代码可以完成任务:

AtomicReference<BigDecimal> doReqQtySum = new AtomicReference<>(BigDecimal.ZERO);
AtomicReference<BigDecimal> boQtySum = new AtomicReference<>(BigDecimal.ZERO);
List<DoRequestDetailModel> tempList = new ArrayList<>();
models.forEach(detail -> {
    doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -> bg1.add(bg2));
    boQtySum.accumulateAndGet(detail.getBoQty(), (bg1, bg2) -> bg1.add(bg2));
    if(detail.getIsOriginProduct()) {
         tempList.add(detail);
    }
});

是否有更好的解决方案?

英文:

I try to do two things at once:<br/>

1.Sum values from specific field of the objects in a list

AtomicReference&lt;BigDecimal&gt; doReqQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
AtomicReference&lt;BigDecimal&gt; boQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
models.forEach(detail -&gt; {
	doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -&gt; bg1.add(bg2));
	boQtySum.accumulateAndGet(detail.getBoQty(),  (bg1, bg2) -&gt; bg1.add(bg2));
});

2.Filter a object from list

DoRequestDetailModel originProductRequestDetail = models.stream()
	.filter(m -&gt; m.getIsOriginProduct())
	.reduce((a, b) -&gt; {
	     throw new IllegalStateException();
	})
    .get();

I would like this code, but it dosen't work:

AtomicReference&lt;BigDecimal&gt; doReqQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
AtomicReference&lt;BigDecimal&gt; boQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
DoRequestDetailModel originProductRequestDetail = new DoRequestDetailModel();
models.forEach(detail -&gt; {
	 doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -&gt; bg1.add(bg2));
	 boQtySum.accumulateAndGet(detail.getBoQty(),  (bg1, bg2) -&gt; bg1.add(bg2));
	 if(detail.getIsOriginProduct()) {
	      originProductRequestDetail = detail;
	 }
});

The next code could be done

AtomicReference&lt;BigDecimal&gt; doReqQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
AtomicReference&lt;BigDecimal&gt; boQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
List&lt;DoRequestDetailModel&gt; tempList = new ArrayList&lt;&gt;();
models.forEach(detail -&gt; {
	doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -&gt; bg1.add(bg2));
	boQtySum.accumulateAndGet(detail.getBoQty(),  (bg1, bg2) -&gt; bg1.add(bg2));
	if(detail.getIsOriginProduct()) {
	     tempList.add(detail);
	}
});

Is there a better solution ?

答案1

得分: 1

你还需要使用`AtomicReference`:

    AtomicReference<BigDecimal> doReqQtySum = new AtomicReference<>(BigDecimal.ZERO);
    AtomicReference<BigDecimal> boQtySum = new AtomicReference<>(BigDecimal.ZERO);
    AtomicReference<DoRequestDetailModel> originProductRequestDetail = new AtomicReference<>(new DoRequestDetailModel());
    models.forEach(detail -> {
         doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -> bg1.add(bg2));
         boQtySum.accumulateAndGet(detail.getBoQty(),  (bg1, bg2) -> bg1.add(bg2));
         if(detail.getIsOriginProduct()) {
              if(originProductRequestDetail.get()) throw new IllegalStateException();
              originProductRequestDetail.set(detail);
         }
    });

这是因为你在`forEach`内部调用的函数范围中更改了外部变量的引用。[这里][1]有一个相关的问题。

  [1]: https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value
英文:

You would have to use AtomicReference as well:

AtomicReference&lt;BigDecimal&gt; doReqQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
AtomicReference&lt;BigDecimal&gt; boQtySum = new AtomicReference&lt;&gt;(BigDecimal.ZERO);
AtomicReference&lt;DoRequestDetailModel&gt; originProductRequestDetail = new AtomicReference&lt;&gt;(new DoRequestDetailModel());
models.forEach(detail -&gt; {
     doReqQtySum.accumulateAndGet(detail.getDoReqQty(), (bg1, bg2) -&gt; bg1.add(bg2));
     boQtySum.accumulateAndGet(detail.getBoQty(),  (bg1, bg2) -&gt; bg1.add(bg2));
     if(detail.getIsOriginProduct()) {
          if(originProductRequestDetail.get()) throw new IllegalStateException();
          originProductRequestDetail.set(detail);
     }
});

It's because you're changing a reference of an outside variable in the scope of the function invoked inside forEach. Here is a related question.

huangapple
  • 本文由 发表于 2020年3月16日 17:42:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/60703560.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定