[英]How to use Java Stream Map a List<Src> to an Object Tgt?
First of all, this question is from my real work.首先,这个问题来自我的真实工作。 I need to solve it and I do some trade off working to realize it unperfect.
我需要解决它,并且我会做一些权衡工作以实现它不完美。 What's more, I abstract and simplify the basic data structure to avoid sensitive data.
更重要的是,我对基本数据结构进行了抽象和简化,以避免敏感数据。
My source and target Object which is Src
and Tgt
is as followed:我的源和目标 Object 是
Src
和Tgt
如下:
public class Src {
int id;
boolean flag;
int count;
// Constructor Getter and Setter methods......
}
public class Tgt {
int id;
int true_count;
int false_count;
// Constructor Getter and Setter methods......
}
Now I have a list of Src Object named srcList
.现在我有一个名为
srcList
的 Src Object 列表。 I want to combine the list to one Object named Tgt
which operation is like count how many flag
in Src
Object is true or false in the list.我想将列表组合到一个名为
Tgt
的 Object 中,该操作类似于计算Src
Object 中的多少个flag
在列表中是真还是假。
In java8 Stream, I haved tried to use flatmap
and map
operator to convert List to one Object like followed code:在 java8 Stream 中,我尝试使用
flatmap
和map
运算符将 List 转换为一个 Object,如下代码:
public class TestSrc2Tgt {
public List<Tgt> listSrc2ListTgt(List<Src> srcList){
Tgt tgt = new Tgt();
for(Src src : srcList) {
tgt.setId(src.getId());
if(src.isFlag()) {
tgt.setTrue_count(tgt.getTrue_count + src.getCount());
}
else {
tgt.setFalse_count(tgt.getFalse_count + src.getCount());
}
}
/* it is my question and I do not want to use List
What I want is convert List<Src> to Tgt which is a kind of combine srcList
*/
List<Tgt> tgtList = new ArrayList<>();
tgtList.add(tgt);
return tgtList;
}
public static void main(String[] args){
TestSrc2Tgt testSrc2Tgt = new TestSrc2Tgt();
List<Src> srcList = new ArrayList<>();
srcList.add(new Src(1, true, 15));
srcList.add(new Src(1, false, 20));
srcList.add(new Src(1, false, 110));
srcList.add(new Src(2, true, 40));
srcList.add(new Src(2, false, 250));
srcList.add(new Src(2, true, 420));
// 1. Cluster the id and generate list by id
Map<Integer, List<Src>> srcMap = srcList.stream()
.collect(Collectors.groupingBy(Src::getId, Collectors.toList()));
// 2. Count how many Src flag is true or false by listSrc2ListTgt method
// Where I hate is here
List<Tgt> tgtMaplist =
srcMap.entrySet().stream().map( e -> {return testSrc2Tgt.listSrc2ListTgt(e.getValue());})
.map(item->item.get(0)).collect(Collectors.toList());
tgtMaplist.stream().forEach(t -> {
System.out.println(t.getId());
System.out.println(t.getFalse_count());
System.out.println(t.getTrue_count());
});
}
}
The code could really work but I hate the listSrc2ListTgt()
method and .map(item->item.get(0))
in getting List<Tgt> tgtMaplist
.该代码确实可以工作,但我讨厌
listSrc2ListTgt()
方法和.map(item->item.get(0))
获取List<Tgt> tgtMaplist
。 My idea is a one way converting from List<Src>
to Tgt
but not convert from List<Src>
to List<Tgt>
(only one element) and then get Tgt
from List<Tgt>
.我的想法是从
List<Src>
转换为Tgt
但不从List<Src>
转换为List<Tgt>
(只有一个元素)然后从List<Tgt>
获取Tgt
的一种方法。 It is a wasted and unperfect way of realizing .这是一种浪费且不完美的实现方式。
In ideal situation理想情况下
public Tgt listSrc2ListTgt(List<Src> srcList){
Tgt tgt = new Tgt();
for(Src src : srcList) {
tgt.setId(src.getId());
if(src.isFlag()) {
tgt.setTrue_count(src.getCount());
}
else {
tgt.setFalse_count(src.getCount());
}
}
return tgt;
}
And in main method:在主要方法中:
List<Tgt> tgtMaplist = srcMap.entrySet().stream()
.map( e -> {return testSrc2Tgt.listSrc2ListTgt(e.getValue());})
.collect(Collectors.toList());
It is more comfortable.它更舒适。 Because there is no intermediate process taken from the tgtlist by using
.get(0)
method, which seems less redundant.因为没有使用
.get(0)
方法从 tgtlist 中取出中间过程,这似乎不太冗余。 However the ideal code is unable to pass the compiler.然而理想的代码是无法通过编译器的。
Your ideal solution should work, if you return tgt
instead of tgtList
inside ListSrc2ListTgt
:如果您在
ListSrc2ListTgt
中返回tgt
而不是tgtList
,那么您理想的解决方案应该可以工作:
public Tgt ListSrc2ListTgt(List<Src> srcList) {
Tgt tgt = new Tgt();
for(Src src : srcList) {
tgt.setId(src.getId());
if(src.isFlag()) {
tgt.setTrue_count(src.getCount());
} else {
tgt.setFalse_count(src.getCount());
}
}
return tgt;
}
And to improve the readability and usability of your stream in main
method:并在
main
方法中提高 stream 的可读性和可用性:
entrySet()
if you don't need the keys.entrySet()
。 use values()
instead.values()
代替。List<Tgt> tgtMaplist = srcMap.values().stream()
.map(testSrc2Tgt::ListSrc2ListTgt)
.collect(Collectors.toList());
You can map your Src
into Tgt
first.您可以先将您的
Src
map 放入Tgt
。 Then using toMap
map by id
and merge the list of Tgt
into one using the merge function.然后使用
toMap
map by id
并使用合并 function 将Tgt
列表合并为一个。 Then get the map values in an ArrayList.然后在 ArrayList 中获取 map 值。
Map<Integer, Tgt> tgtMap = srcList.stream()
.map(s -> new Tgt(s.getId(),
(s.isFlag() ? s.getCount() : 0),
(s.isFlag() ? 0 : s.getCount())))
.collect(Collectors.toMap(Tgt::getId, e -> e,
(a, b) -> new Tgt(a.getId(),
a.getTrue_count() + b.getTrue_count(),
a.getFalse_count() + b.getFalse_count())));
List<Tgt> tgtList = new ArrayList(tgtMap.values()); // Create list from map values
Here using map()
transform Src
object into Tgt
object这里使用
map()
将Src
object 转换为Tgt
object
s -> new Tgt(s.getId(), (s.isFlag() ? s.getCount() : 0),(s.isFlag() ? 0 :s.getCount()))
And then using Collectors.toMap
map by Tgt::getId
which is first parameter of toMap
.The next one is the key of the map e -> e
for the whole Tgt
obj as key.然后使用
Collectors.toMap
map by Tgt Tgt::getId
这是toMap
的第一个参数。下一个是 map e -> e
的键,用于整个Tgt
obj 作为键。 And finally last parameter in the merge function will used for merging two Tgt
objects into one Tgt
object so that all values of the same key merged.最后,合并中的最后一个参数 function 将用于将两个
Tgt
对象合并为一个Tgt
object 以便同一键的所有值合并。
Collectors.toMap(Tgt::getId,
e -> e,
(a, b) -> new Tgt(a.getId(),
a.getTrue_count() + b.getTrue_count(),
a.getFalse_count() + b.getFalse_count()))
To simplify the code with explanations用解释简化代码
Map<Integer, Tgt> tgtMap = srcList.stream()
.map(s -> /*transform into Tgt*/)
.collect(Collectors.toMap(Tgt::getId, e -> e, /*Merge function*/));
Not strictly an answer to your exact question, but a simpler approach to find the 3 things asked for:严格来说不是对您的确切问题的回答,而是一种更简单的方法来找到所要求的 3 件事:
int id = srcList.get(srcList.size() - 1).getId();
int true_count = srcList.stream().filter(Src::getFlag).count();
int false_count = srcList.size() - true_count;
Here is a pretty simply way using Java streams:这是使用 Java 流的一种非常简单的方法:
List<Src> srcList = new ArrayList<Src>();
srcList.add(new Src(1, true, 15));
srcList.add(new Src(1, false, 20));
srcList.add(new Src(1, false, 110));
srcList.add(new Src(2, true, 40));
srcList.add(new Src(2, false, 250));
srcList.add(new Src(2, true, 420));
List<Tgt> tgtList = srcList.stream()
.collect(Collectors.groupingBy(Src::getId, Collectors.toList()))
.entrySet().stream()
.map(e ->
new Tgt(e.getKey(),
e.getValue().stream().mapToInt(s -> s.getFlag() ? s.getCount() : 0).sum(),
e.getValue().stream().mapToInt(s -> !s.getFlag() ? s.getCount() : 0).sum())
).collect(Collectors.toList());
System.out.println(tgtList);
Here is the output:这是 output:
[Tgt [id=1, true_count=15, false_count=130], Tgt [id=2, true_count=460, false_count=250]]
EDIT: I would prefer to not use streams so we don't loop over the data so many times.编辑:我宁愿不使用流,所以我们不会多次循环数据。
Map<Integer, Tgt> tgtMap = new HashMap<Integer, Tgt>();
for (Src src: srcList) {
Tgt tgt = new Tgt(src.getId(), src.getFlag() ? src.getCount() : 0, !src.getFlag() ? src.getCount() : 0);
tgtMap.compute(src.getId(), (k, v) ->
v == null ? tgt
: new Tgt(src.getId(),
tgt.getTrue_count() + v.getTrue_count(),
tgt.getFalse_count() + v.getFalse_count()));
}
For this code you need a Pair
class - either from a library, or you can create it yourself.对于此代码,您需要一
Pair
class - 来自库,或者您可以自己创建它。 Instead of custom collector you could use Collectors.partitioningBy
, but this way it's more efficient.您可以使用
Collectors.partitioningBy
代替自定义收集器,但这样效率更高。
List<Tgt> tgtList = srcList.stream()
.collect(Collectors.groupingBy(Src::getId, Collector.of(
() -> new Pair<>(0L, 0L),
(pair, src) -> {
if (src.isFlag())
pair.setKey(pair.getKey() + src.getCount());
else
pair.setValue(pair.getValue() + src.getCount());
},
(pairA, pairB) -> new Pair<>(pairA.getKey() + pairB.getKey(), pairA.getValue() + pairB.getValue())
)))
.entrySet()
.stream()
.map(entry -> new Tgt(entry.getKey(), (int) (long) entry.getValue().getKey(), (int) (long) entry.getValue().getValue()))
.collect(Collectors.toList());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.