Java Stream 在单循环中以声明方式分别按多个字段进行分组

2024-03-14

我用谷歌搜索了它,但我主要发现了按聚合字段分组或改变流响应的情况,但没有找到下面的情况:

我有课User有字段category and marketingChannel.

我必须以声明式风格编写一个方法,该方法接受用户列表并根据category并且还基于marketingChannel单独(即不groupingBy(... ,groupingBy(..)) ).

我无法在一个循环中完成它。这就是我必须达到的目标。

我编写了一些方法如下:

import java.util.*;
import java.util.stream.*;
public class Main
{
    public static void main(String[] args) {
        List<User> users = User.createDemoList();
        imperative(users);
        declerativeMultipleLoop(users);
        declerativeMultipleColumn(users);
    }
    
    public static void imperative(List<User> users){
        Map<String, Integer> categoryMap = new HashMap<>();
        Map<String, Integer> channelMap = new HashMap<>();
        for(User user : users){
           Integer  value = categoryMap.getOrDefault(user.getCategory(), 0);
           categoryMap.put(user.getCategory(), value+1);
           value = channelMap.getOrDefault(user.getMarketingChannel(), 0);
           channelMap.put(user.getMarketingChannel(), value+1);
        }
        System.out.println("imperative");
        System.out.println(categoryMap);
        System.out.println(channelMap);
    }
    
    public static void declerativeMultipleLoop(List<User> users){
        Map<String, Long> categoryMap = users.stream()
        .collect(Collectors.groupingBy(
            User::getCategory, Collectors.counting()));
        Map<String, Long> channelMap = users.stream()
        .collect(Collectors.groupingBy(
            User::getMarketingChannel, Collectors.counting()));
        System.out.println("declerativeMultipleLoop");
        System.out.println(categoryMap);
        System.out.println(channelMap);
    }
    
    public static void declerativeMultipleColumn(List<User> users){
        Map<String, Map<String, Long>> map = users.stream()
        .collect(Collectors.groupingBy(
            User::getCategory,
            Collectors.groupingBy(User::getMarketingChannel, 
            Collectors.counting())));
       
        System.out.println("declerativeMultipleColumn");
        System.out.println("groupingBy category and marketChannel");
        System.out.println(map);
        
        Map<String, Long> categoryMap = new HashMap<>();
        Map<String, Long> channelMap = new HashMap<>();
        
        for (Map.Entry<String, Map<String, Long>> entry: map.entrySet()) {
            String category = entry.getKey();
            Integer count = entry.getValue().size();
            Long value = categoryMap.getOrDefault(category,0L);
            categoryMap.put(category, value+count);
            for (Map.Entry<String, Long> channelEntry : entry.getValue().entrySet()){
                String channel = channelEntry.getKey();
                Long channelCount = channelEntry.getValue();
                Long channelValue = channelMap.getOrDefault(channel,0L);
                channelMap.put(channel, channelValue+channelCount);
            }
        }
        System.out.println("After Implerative Loop on above.");
        System.out.println(categoryMap);
        System.out.println(channelMap);
    }
}
class User{
    private String name;
    private String category;
    private String marketChannel;
    
    public User(String name, String category, String marketChannel){
        this.name = name;
        this.category = category;
        this.marketChannel = marketChannel;
    }
    public String getName(){
        return this.name;
    }
    public String getCategory(){
        return this.category;
    }
    public String getMarketingChannel(){
        return this.marketChannel;
    }
    
     @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(name, user.name) &&
                Objects.equals(category, user.category) &&
                Objects.equals(marketChannel, user.marketChannel);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, category, marketChannel);
    }
    public static List<User> createDemoList(){
        return Arrays.asList(
            new User("a", "student","google"),
            new User("b", "student","bing"),
            new User("c", "business","google"),
            new User("d", "business", "direct")
            );
    }

方法declerativeMultipleLoop是声明性的,但每个字段都有一个单独的循环。复杂度:O(noOfFields * 用户数量)

问题在于declerativeMultipleColumn方法,因为我最终编写了命令式代码和多个循环。

我想以完全声明性且尽可能高效的方式编写上述方法。即复杂度:O(用户数量)

示例输出:

至关重要的
{企业=2,学生=2}
{直接=1、谷歌=2、必应=1}
声明式多重循环
{企业=2,学生=2}
{直接=1、谷歌=2、必应=1}
声明式多列
按类别和市场渠道分组
{business={direct=1, google=1}, Student={google=1, bing=1}}
在上面的命令循环之后。
{企业=2,学生=2}
{直接=1、谷歌=2、必应=1}


如果我理解您的要求,那就是使用单个流操作来生成 2 个单独的映射。这将需要一个结构来保存地图和一个收集器来构建该结构。像下面这样:

class Counts {
    public final Map<String, Integer> categoryCounts = new HashMap<>();
    public final Map<String, Integer> channelCounts = new HashMap<>();

    public static Collector<User,Counts,Counts> countsCollector() {
        return Collector.of(Counts::new, Counts::accept, Counts::combine, CONCURRENT, UNORDERED);
    }

    private Counts() { }

    private void accept(User user) {
        categoryCounts.merge(user.getCategory(), 1, Integer::sum);
        channelCounts.merge(user.getChannel(), 1, Integer::sum);
    }

    private Counts combine(Counts other) {
        other.categoryCounts.forEach((c, v) -> categoryCounts.merge(c, v, Integer::sum));
        other.channelCounts.forEach((c, v) -> channelCounts.merge(c, v, Integer::sum));
        return this;
    }
}

然后可以将其用作收集器:

Counts counts = users.stream().collect(Counts.countsCollector());
counts.categoryCounts.get("student")...

(仅供参考:在这种情况下,命令式和声明式之间的区别是相当任意的。对我来说,定义流操作感觉非常程序化(与 Haskell 中的等效操作相反))。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java Stream 在单循环中以声明方式分别按多个字段进行分组 的相关文章

随机推荐

  • 正确处理文件流和二进制流以及处理文件流

    事实上 我尝试对我的代码进行防错 但最终使它看起来相当混乱 我设置了一个函数来读取某种类型的文件 我希望函数在出现问题时返回 false 如果一切正常则返回 true 我无法弄清楚如何构建一切 我有一个尝试打开文件流的初始 try catc
  • 在 ServiceProvider 中使用 Entity Framework Core 3.1 和 UseInMemoryDatabase 选项(作用域生命周期)

    我已将一个 Web 应用程序项目从 NET Core 2 1 迁移到 3 1 也将 EF Core 从 2 1 1 迁移到 3 1 0 迁移后 一些单元测试不再工作 抛出重复键数据库异常 我模拟了这个问题并意识到 EF core 带有选项U
  • 带有远程文件的 HTML5 文件 API

    我尝试了几个小时使用 HTML5 文件系统添加带有 URL 的远程文件 例如http example com doc pdf http example com doc pdf 而不是通过文件输入获得的文件 因为我希望该过程是自动的 我有多个
  • 在 eclipselink 中设置隔离级别

    我想使用 eclipse 链接设置隔离级别 我尝试了这两种方法来做到这一点 java sql Connection mgr EMF get createEntityManager tx mgr getTransaction tx begin
  • Android Studio 与 Transfuse

    我可以在我的 android 项目中成功设置 Transfuse 但是当使用 Android Studio 运行该应用程序时 它失败了 可能是因为 Manifest xml 必须为空才能让 Transfuse 处理 有人曾经把这些一起工作过
  • 通过ViewBag传递模型对象

    我想知道是否可以通过 ViewBag 传递模型对象 我尝试了以下代码 但不知何故在我的视图中 它仅显示模型的路径 控制器 public ActionResult Tempo DateTime date1 new DateTime 1990
  • 安装签名的 msi 安装程序时出现奇怪的“程序名称”[重复]

    这个问题在这里已经有答案了 登录 MSI 安装程序后 我遇到以下问题 我正在使用signtool exe并且msi文件签名正常 但是当我测试它时 显示我公司名称的UAC确认对话框显示55847 msi的 程序名称 而不是我的安装文件的名称
  • Haskell - 如何基于二叉树的foldr创建mapTree函数?

    这是 Haskell 编程的第一原理 第 11 章代数数据类型中的一个问题 data BinaryTree a Leaf Node BinaryTree a a BinaryTree a deriving Eq Ord Show 我们实际上
  • 是否可以将 Flume 标头写入 HDFS 接收器并删除主体?

    text with headers 序列化器 HDFS 接收器序列化器 允许保存 Flume 事件标头而不是丢弃它们 输出格式由标头 后跟空格和正文有效负载组成 我们想删除正文并仅保留标题 对于 HBase 接收器 RegexHbaseEv
  • 使用 XDebug 设置 IntelliJ Idea 远程调试

    我连续几天尝试在 IntelliJ Idea 和 Xdebug 中设置远程调试 但没有成功 我有一个 Windows 7 主机和 VM VirtualBox 使用 Windows XP 作为 Web 服务器 它安装了 WampServer
  • 处理信号后如何停止从键盘读取?

    我正在编写一个程序 其中 SIGINT 信号在第一次发送时进行处理 但之后设置为默认值 例如 我有这个 static volatile int stop terminating 1 void handler int dummy stop t
  • 删除 MySQL 中列的最后两个字符

    我有一个 SQL 列 其中的条目是字符串 我需要在修剪最后两个字符后显示这些条目 例如如果条目是199902345它应该输出1999023 我尝试查看 TRIM 但看起来只有当我们知道最后两个字符是什么时 它才会提供修剪 但就我而言 我不知
  • 流星铁路由器嵌套路由

    我有两个具有一对多关系的流星集合 建筑物和空间 在我的建筑页面上 我想显示与建筑相关的空间 现在 我是这样做的 buildingsRoute coffee BuildingController RouteController extend
  • Ruby 1.9 Base64 编码写入文件错误

    我收到以下错误 Encoding UndefinedConversionError x81 from ASCII 8BIT to UTF 8 下面的内容都在 Ruby 1 8 7 中工作 所以我认为这与 Ruby 1 9 处理编码的方式有关
  • 从 MAC 转换为 IPv6

    我想了解如何将 MAC 地址转换为 IPv6 地址 例如 00 01 04 76 2A 5C 应该成为 FE80 0201 04FF FE76 2A5C 有人可以详细说明一下转换吗 我们假设本地机器的自动配置没有随机参数 逐步转换 http
  • 独立应用程序中的 Spring Security

    如何在独立应用程序中使用 Spring Security 我只需要使用 Spring Security 的身份验证部分 我需要根据 Windows Active Directory 对用户进行身份验证 网络上有很多在 Servlet 中使用
  • 如何在 3 维空间中找到凸包

    给定一组点S x y z 如何找到convex hull那些点 我尝试理解该算法here http wcipeg com wiki Convex hull 但拿不到太多 It says 首先将所有点投影到 xy 平面上 并通过选择具有最高
  • Objective-C 中面向方面的 HTTP 身份验证示例

    我正在寻找一些示例 技巧 建议以及一些用于实现 或查找其实现 面向方面的 HTTP 身份验证库的一般方向感 作为一些基础工作 我们构建了一个 iOS 库 该库为 HTTP 服务建立各种形式的身份验证 通过 Web 表单或本机模式窗口请求用户
  • Python 3 中 getstatusoutput 的替换

    由于命令模块自 Python 2 6 起已被弃用 我正在寻找替换的最佳方法命令 getstatusoutput http docs python org library commands html highlight commands co
  • Java Stream 在单循环中以声明方式分别按多个字段进行分组

    我用谷歌搜索了它 但我主要发现了按聚合字段分组或改变流响应的情况 但没有找到下面的情况 我有课User有字段category and marketingChannel 我必须以声明式风格编写一个方法 该方法接受用户列表并根据category