简体   繁体   English

如何使用 Java Stream Map 列表<src>到 Object Tgt?</src>

[英]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 是SrcTgt如下:

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 中,我尝试使用flatmapmap运算符将 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 的可读性和可用性:

  • don't use entrySet() if you don't need the keys.如果不需要密钥,请不要使用entrySet() use values() instead.使用values()代替。
  • the lambda can then be shortened with a method reference.然后可以使用方法参考缩短 lambda。
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());

如何转换列表<object>至 Map<k, v> 使用 java stream<div id="text_translate"><p> 我想将List&lt;ObjectInList&gt;转换为Map&lt;K, V&gt;</p><pre> class ObjectInList { List&lt;Long&gt; listWithLong; Map&lt;String, Object&gt; dataMap; // there is 'id' key, and i want to use this id as key in map to be converted }</pre><p> 新的 map 格式如下</p><pre>String type; // this type is value of dataMap. List&lt;Long&gt; contents</pre><p> List&lt;Object&gt;中的每个 Object 都可以有重复的类型</p><p>例如</p><pre>///////// before converted //////////// [ { list: [1,2,3], dataMap: { type: "a", } }, { list: [4,5,6], dataMap: { type: "b", } }, { list: [7,8], dataMap: { type: "a", } }, ] ///////////// after converted ////////// { "a": [1,2,3,7,8], "b": [4,5,6] }</pre></div></k,></object> - How to convert LIst<Object> to Map<K, V> with using java stream

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何获取地图<String, List<Object> &gt; 来自 Java 8 中的流 - How get a Map<String, List<Object>> from a Stream in Java 8 如何转换列表<object>至 Map<k, v> 使用 java stream<div id="text_translate"><p> 我想将List&lt;ObjectInList&gt;转换为Map&lt;K, V&gt;</p><pre> class ObjectInList { List&lt;Long&gt; listWithLong; Map&lt;String, Object&gt; dataMap; // there is 'id' key, and i want to use this id as key in map to be converted }</pre><p> 新的 map 格式如下</p><pre>String type; // this type is value of dataMap. List&lt;Long&gt; contents</pre><p> List&lt;Object&gt;中的每个 Object 都可以有重复的类型</p><p>例如</p><pre>///////// before converted //////////// [ { list: [1,2,3], dataMap: { type: "a", } }, { list: [4,5,6], dataMap: { type: "b", } }, { list: [7,8], dataMap: { type: "a", } }, ] ///////////// after converted ////////// { "a": [1,2,3,7,8], "b": [4,5,6] }</pre></div></k,></object> - How to convert LIst<Object> to Map<K, V> with using java stream 将 object 列表转换为 map java 8 ZF7B44CFAFD5C52223D7BZBZ - convert list of object to map java 8 stream 用于 Map 的 Java 8 流过滤器<String, List<Object> &gt; - Java 8 stream filter for Map<String, List<Object>> 如何使用 Java 流列表进行转换<Map<String,Object> &gt; 列出<String>删除每个流的一部分 - How to convert with Java stream List<Map<String,Object>> to List<String> removing part of each Stream 使用Java8流将Object减少为Map - Use Java8 stream to reduce Object to a Map java流列表 <Object[]> 到地图 <Object, List<Object[]> &gt; - java stream List<Object[]> to Map<Object, List<Object[]>> How do you use java stream api to convert list of objects into a nested map based on information stored inside object? - How do you use java stream api to convert list of objects into a nested map based on information stored inside object? 使用流将Java <Object>收集到Java 8中的Map <String,List <Object >> - Collect List<Object> to Map<String, List<Object>> in Java 8 using stream 用流映射的Java 8列表 - Java 8 list to map with stream
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM