V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zealinux
V2EX  ›  Java

求教 Java8 stream 代码

  •  
  •   zealinux · 2020-02-25 01:51:34 +08:00 · 3688 次点击
    这是一个创建于 1727 天前的主题,其中的信息可能已经有所发展或是发生改变。

    下面是已经从数据库 Sample 表取出得数据

    uid date 	hour	clicks	views
    1  02-10	18		1		2
    1  02-10	19		2		2
    1  02-19	08		3		2
    2  02-11	18		10		2
    2  02-11	19		20		2
    

    现在想要 [ group by ] ( uid, date ), 然后 clicks 求和,views 求和,hour 这一列不要了。

    uid date 		clicks	views
    1  02-10		3		4
    1  02-19		3		2
    2  02-11		30		4
    

    另外,如果一个 object 还有其他的字段,groupby 的时候不一定是求和,可能是其他运算。

    晚上搜了一大堆都是简单的一个字段的 groupBy,像下面一样,且都是返回某个属性值 Long,

    Map<String, Long> countMap = samples.stream()
    	.collect(
    		Collectors.groupingBy(o -> o.getUid(),
    			Collectors.summingLong(Sample::getViews)));
    

    其实我想得到对象(比如 sample 对象),求教

    18 条回复    2020-02-26 22:59:36 +08:00
    lhx2008
        1
    lhx2008  
       2020-02-25 07:28:50 +08:00 via Android
    所以要输出什么,你的对象 POJO 怎么写的?
    sumarker
        2
    sumarker  
       2020-02-25 08:10:58 +08:00
    没看懂你的问题是什么?
    Malthael
        3
    Malthael  
       2020-02-25 08:16:51 +08:00
    groupingBy 第二个参数你得自定义合并规则。
    zealinux
        4
    zealinux  
    OP
       2020-02-25 09:25:09 +08:00
    @sumarker

    其实我就是想要的是,类似 MySQL 的 group by 两个字段功能,且有些字段是 sum() 操作。

    ```
    Map<String, Sample> countMap = samples.stream()
    .collect(
    Collectors.groupingBy(
    (这里 groupBy 两个字段,uid, 和 date )
    (这里返回是 Sample 对象));
    ```
    zealinux
        5
    zealinux  
    OP
       2020-02-25 09:27:26 +08:00
    @lhx2008

    我要输出 group by (两个字段) 后的结果集合 List<Sample>,

    其余部分字段是累加。

    ----
    这个表如果在 MySQL 里,SQL 会比较简单。
    lhx2008
        6
    lhx2008  
       2020-02-25 09:38:49 +08:00 via Android
    请问你 MAP<string,sample>怎么做到你的意思,先把返回值的类型确定一下
    zhady009
        7
    zhady009  
       2020-02-25 10:54:23 +08:00
    用 toMap 就行了然后第 3 个参数 BinaryOperator 里面 key 一样时你把 clicks 和 views 相加
    qinxi
        8
    qinxi  
       2020-02-25 11:30:09 +08:00
    samples.stream()
    .collect(
    Collectors.groupingBy(T::getId)其实返回值就是 Map<String,List<T>> 你要的 sample 不就在 Map 的 List 里面吗...
    hrong
        9
    hrong  
       2020-02-25 13:44:32 +08:00
    楼主就是来要代码的。。。

    <pre>
    public static void main(String[] args) {
    Sample s1 = new Sample();
    s1.setUuid(1);
    s1.setDate("2-10");
    s1.setHour(18);
    s1.setClicks(1);
    s1.setViews(2);

    Sample s2 = new Sample();
    s2.setUuid(1);
    s2.setDate("2-10");
    s2.setHour(19);
    s2.setClicks(2);
    s2.setViews(2);

    Sample s3 = new Sample();
    s3.setUuid(1);
    s3.setDate("2-19");
    s3.setHour(8);
    s3.setClicks(3);
    s3.setViews(2);

    Sample s4 = new Sample();
    s4.setUuid(2);
    s4.setDate("2-11");
    s4.setHour(18);
    s4.setClicks(10);
    s4.setViews(2);

    Sample s5 = new Sample();
    s5.setUuid(2);
    s5.setDate("2-11");
    s5.setHour(19);
    s5.setClicks(20);
    s5.setViews(2);

    Collection<Sample> list = List.of(s1,s2,s3,s4,s5)
    .stream()
    .collect(collectingAndThen(toMap((Sample s) -> s.getUuid() + "_" + s.getDate(),
    Function.identity(),
    (Sample s, Sample a) -> {
    int sumClick = s.getClicks() + a.getClicks();
    int sumView = s.getViews() + a.getViews();
    Sample result = new Sample();
    result.setUuid(s.getUuid());
    result.setDate(s.getDate());
    result.setClicks(sumClick);
    result.setViews(sumView);
    return result;
    }), s -> s.values()));
    list.forEach(System.out::println);
    }
    </pre>
    jimotudou
        10
    jimotudou  
       2020-02-25 14:10:23 +08:00
    9 楼的可以满足楼主需求,但是你为何不直接在数据库写好 sql,在映射到实体类呢。
    chanchan
        11
    chanchan  
       2020-02-25 14:22:18 +08:00
    Map<Integer, Map<String, Map<String,Integer>>> collect2 = list.stream().
    collect(Collectors.groupingBy(Sample::getUid,
    Collectors.toMap(Sample::getDate,v -> {
    HashMap<String,Integer> map = new HashMap<String,Integer>();
    map.put("clicks", v.getClicks()==null?0:v.getClicks());
    map.put("views", v.getViews()==null?0:v.getViews());
    return map;
    },(a,b) -> {
    a.put("clicks", a.get("clicks")+a.get("clicks"));
    b.put("views", a.get("views")+a.get("views"));
    return a;
    })));

    想了好一会儿...
    Rwing
        12
    Rwing  
       2020-02-25 14:40:44 +08:00
    c#欢迎你
    https://dotnetfiddle.net/zUSwU7


    ```
    var data = new List<Sample>
    {
    new Sample {id = 1, date = "02-10", clicks = 1, views = 2},
    new Sample {id = 1, date = "02-10", clicks = 2, views = 2},
    new Sample {id = 1, date = "02-19", clicks = 3, views = 2},
    new Sample {id = 2, date = "02-11", clicks = 4, views = 2},
    new Sample {id = 2, date = "02-11", clicks = 5, views = 2},
    };

    var result = data.GroupBy(i => new { i.id, i.date })
    .Select(g => new Sample
    {
    id = g.Key.id,
    date = g.Key.date,
    clicks = g.Sum(i => i.clicks),
    views = g.Sum(i => i.views),
    });
    ```
    hrong
        13
    hrong  
       2020-02-25 16:41:12 +08:00
    @jimotudou 楼主就是来要代码的,我写个 hello world 就够了,还要服务到 DAO 层啊? 也不问问他充够钱了吗。。。
    hrong
        14
    hrong  
       2020-02-25 16:43:14 +08:00
    @Rwing C#简洁多了,果然走在前头。。。Java 不思进取好多年了。。。
    coer
        15
    coer  
       2020-02-26 10:54:34 +08:00
    @hrong hhh 牛皮,这几个函数我还是第一见
    hrong
        16
    hrong  
       2020-02-26 12:17:45 +08:00 via iPhone
    @coer 楼主拿完代码就不见了。已 block
    zealinux
        17
    zealinux  
    OP
       2020-02-26 22:35:16 +08:00
    @hrong 不要 block 我,这几天都是昏天黑地的晚上上班。(到家都半夜了)
    zealinux
        18
    zealinux  
    OP
       2020-02-26 22:59:36 +08:00
    @hrong
    刚刚才验证了你的代码。居然 OK👌。
    (只是暂时还是看不懂。)
    Thanks a lot.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1365 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:56 · PVG 01:56 · LAX 09:56 · JFK 12:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.