stream-实践应用-统计分析

背景

  • 业务部门提供了一个数据,数据甚至不是excel类型的,是data.txt,每一行都是一个数据,需要对此数据进行统计分析

统计各个月份的销量

  • 因为直接获取resources下的data.txt,所以要借助输入流进行获取数据,再通过BufferedReader 将数据传递给流
  • 想要对key进行排序,推荐使用TreeMap
public class Main4 {

    static final int INDEX = 0; // 索引
    static final int DATETIME = 1;  // 日期
    static final int ORDERID = 2; // 订单号
    static final int ITEMID = 3; // 商品编号
    static final int AMOUNT = 4; // 数量
    static final int PRICE = 5; // 价格
    static final int TOTAL = 6; // 总价


    public static void main(String[] args) {
        try (
                // 读取文件 通过ClassLoader获取文件流
                InputStream is = Main4.class.getClassLoader().getResourceAsStream("data.txt");
                // 将InputStream 装换为 Stream<String>
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
                Stream<String> lines = bufferedReader.lines()
        ) {
            // 测试一下流是否生成
            // lines.forEach(System.out::println);
            // 统计每个月的的订单数量
            // 定义一个格式化模版 用于解析日期
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            lines.map(line ->  line.split(",")) // 将每一行数据转换为数组 分隔符是逗号
                    // 将日期字符串解析为YearMonth对象 用于统计月份
                    // 通过Collectors.groupingBy方法进行分组统计
                    // 第一个参数是分组的key 可以理解为提取器
                    // 第二个参数是容器类型 TreeMap::new 用于指定分组后的Map类型  第二个参数可以不指定,默认是hashMap  但是hashMap是无序的  这里使用TreeMap 有序
                    // 第三个参数是下游收集器 用于统计数量 Collectors.counting()
                    .collect(Collectors.groupingBy((arr -> YearMonth.from( dateTimeFormatter.parse(arr[DATETIME])) ), TreeMap::new, Collectors.counting())) // 统计每个月的订单数量
                    .forEach((k, v) -> System.out.println(k + "月订单数量:" + v));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

获取订单最大的月份

  • 对TreeMap中的entrySet的数值进行遍历,获取最大值
  • max
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
TreeMap<YearMonth, Long> collect = lines.map(line -> line.split(",")) // 将每一行数据转换为数组 分隔符是逗号
        // 将日期字符串解析为YearMonth对象 用于统计月份
        // 通过Collectors.groupingBy方法进行分组统计
        // 第一个参数是分组的key 可以理解为提取器
        // 第二个参数是容器类型 TreeMap::new 用于指定分组后的Map类型  第二个参数可以不指定,默认是hashMap  但是hashMap是无序的  这里使用TreeMap 有序
        // 第三个参数是下游收集器 用于统计数量 Collectors.counting()
        .collect(Collectors.groupingBy((arr -> YearMonth.from(dateTimeFormatter.parse(arr[DATETIME]))), TreeMap::new, Collectors.counting()));// 统计每个月的订单数量

// 对结果进行stream遍历 获取最大值
collect.entrySet().stream().max(Comparator.comparing(Map.Entry::getValue)).ifPresent(System.out::println);

获取销量最高的产品

// 获取销售数量最多的商品
private static void getMaxSaleCount(Stream<String> lines){
    Map<String, Integer> collect = lines.map(line -> line.split(","))
            .collect(Collectors.groupingBy(arr -> arr[ITEMID], Collectors.summingInt(arr -> Integer.parseInt(arr[AMOUNT])))
            );
    collect.entrySet().stream().max(Comparator.comparing(Map.Entry::getValue)).ifPresent(System.out::println);
}

获取销量最高的两个产品

// 获取销售数量前两的商品
private static void getTop2SaleCount(Stream<String> lines){
    Map<String, Integer> collect = lines.map(line -> line.split(","))
            .collect(Collectors.groupingBy(arr -> arr[ITEMID], Collectors.summingInt(arr -> Integer.parseInt(arr[AMOUNT])))
            );
    collect.entrySet().stream().sorted((a,b)->{
        return a.getValue().compareTo(b.getValue()) * -1;
    }).limit(2).forEach(System.out::println);
}
// 获取销售数量前两的商品
private static void getTop2SaleCount(Stream<String> lines){
    Map<String, Integer> collect = lines.map(line -> line.split(","))
            .collect(Collectors.groupingBy(arr -> arr[ITEMID], Collectors.summingInt(arr -> Integer.parseInt(arr[AMOUNT])))
            );
    collect.entrySet().stream().sorted(
            Comparator.comparing(Map.Entry<String,Integer>::getValue).reversed()
            )
    .limit(2).forEach(System.out::println);
    }

使用小顶堆来实现

  • 如果数据量很大,把所有的商品的销售数量统计出来,资源占用很大
  • 可以使用小顶堆,每次只统计最大的10个,当有新的数据进来的时候,和最小的数值进行比较,如果新值大于最小值,则根据小顶堆的规则插入进去

小顶堆的规则:父节点的值小于子节点的值,根节点的值最小

在这里插入图片描述

  • 小顶堆的个数是无限的,我们需要重写一下offer方法
 // 写一个小顶堆的子类,最多存储2个数据
    static class MyQueue<E> extends PriorityQueue<E>{

        private int max;


        public MyQueue(Comparator<? super E> comparator,int max) {
            super(comparator);
            this.max = max;
        }

        @Override
        public boolean offer(E e) {
            boolean r = super.offer(e);
            if(size() > max){
                poll();
            }
            return r;
        }
    }
    // 获取销售数量前两的商品
    private static void getTop2SaleCount(Stream<String> lines){
        Map<String, Integer> collect = lines.map(line -> line.split(","))
                .collect(Collectors.groupingBy(arr -> arr[ITEMID], Collectors.summingInt(arr -> Integer.parseInt(arr[AMOUNT])))
                );
        MyQueue<Map.Entry<String, Integer>> myQueue = collect.entrySet().stream().collect(
                () -> new MyQueue<>(Comparator.comparingInt(Map.Entry<String, Integer>::getValue), 2),
                (queue, entry) -> queue.offer(entry), // 将entry加入到queue中
                (queue1, queue2) -> queue1.addAll(queue2) // 没用并行流,其实这段代码用不上
        );

        while (!myQueue.isEmpty()){
            System.out.println(myQueue.poll());
        }
    }

获取每种商品下单数量最多的用户

  • 先使用商品进行分类,再使用用户进行分类,最后统计数量
// 获取每种商品下单最多的用户 打印 商品 用户 数量
private static void getMaxOfPerItem(Stream<String> lines){
   // 先使用商品进行分类,再使用用户进行分类,最后统计数量
   Map<String, Map<String, Integer>> collect = lines.map(line -> line.split(","))
           .collect(Collectors.groupingBy(arr -> arr[ITEMID],
                   Collectors.groupingBy(arr -> arr[USERID], Collectors.summingInt(arr -> Integer.parseInt(arr[AMOUNT])))
           ));
   // 打印一下每种商品下单的用户以及数量
   // collect.forEach((k, v) -> {
   //    v.entrySet().stream().forEach(entry -> System.out.println(k + " " + entry.getKey() + " " + entry.getValue()));
   // });
   collect.forEach((k, v) -> {
       v.entrySet().stream().max(Comparator.comparing(Map.Entry::getValue)).ifPresent(entry -> System.out.println(k + " " + entry.getKey() + " " + entry.getValue()));
   });
}

获取每种商品下单前二的用户

  • 使用双重groupingBy进行分组
  • 使用自定义的小顶堆,获取每组前两个的用户
private static void getTop2OfPerItem(Stream<String> lines){
        // 先使用商品进行分类,再使用用户进行分类,最后统计数量
        Map<String, Map<String, Integer>> collect = lines.map(line -> line.split(","))
                .collect(Collectors.groupingBy(arr -> arr[ITEMID],
                        Collectors.groupingBy(arr -> arr[USERID], Collectors.summingInt(arr -> Integer.parseInt(arr[AMOUNT])))
                ));
        // 打印一下每种商品下单的用户以及数量
        // collect.forEach((k, v) -> {
        //    v.entrySet().stream().forEach(entry -> System.out.println(k + " " + entry.getKey() + " " + entry.getValue()));
        // });
        collect.forEach((k, v) -> {
            MyQueue<Map.Entry<String, Integer>> myQueue = v.entrySet().stream().collect(
                    () -> new MyQueue<>(Comparator.comparingInt(Map.Entry<String, Integer>::getValue), 2),
                    (queue, entry) -> queue.offer(entry), // 将entry加入到queue中
                    (queue1, queue2) -> queue1.addAll(queue2) // 没用并行流,其实这段代码用不上
            );
            while (!myQueue.isEmpty()){
                System.out.println(k + " " + myQueue.poll());
            }
        });
    }

按照总金额的区间 统计每个区间的数量

// 按照订单总金额的区间划分,统计每个区间的订单数量 小于500 500-1000 1000-1500 1500 以上的
private static void getTotalAmount(Stream<String> lines){
    lines.map(line -> line.split(","))
            .collect(Collectors.groupingBy(arr -> {
                double total = Double.parseDouble(arr[TOTAL]);
                if(total < 500){
                    return "小于500";
                }else if(total < 1000){
                    return "500-1000";
                }else if(total < 1500){
                    return "1000-1500";
                }else {
                    return "1500以上";
                }
            }, Collectors.counting()))
            .forEach((k, v) -> System.out.println(k + " " + v));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/654774.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

初识C语言——第二十六天

函数的递归1 什么是递归呢&#xff1f; 递归的两个必要条件 void print(unsigned int n) {if (n > 9){print(n / 10);}printf("%d ", n % 10); }int main() {unsigned int num 0;scanf("%u", &num);//123//递归-函数自己调用自己print(num);//pr…

Scrapy框架简单介绍及Scrapy项目编写详细步骤(Scrapy框架爬取豆瓣网站示例)

引言 Scrapy是一个用Python编写的开源、功能强大的网络爬虫框架&#xff0c;专为网页抓取和数据提取设计。它允许开发者高效地从网站上抓取所需的数据&#xff0c;并通过一系列可扩展和可配置的组件来处理这些数据。Scrapy框架的核心组成部分包括&#xff1a; Scrapy Engine&…

matplotlib ---词云图

词云图是一种直观的方式来展示文本数据&#xff0c;可以体现出一个文本中词频的使用情况&#xff0c;有利于文本分析&#xff0c;通过词频可以抓住一篇文章的重点 本文通过处理一篇关于分析影响洋流流向的文章&#xff0c;分析影响洋流流向的主要因素都有哪些 文本在文末结尾 …

用手机做客服的吐槽点客服亲们有同感吗

聊天宝手机版很好的解决了&#xff0c;客服手机快速回复客户的需求&#xff0c;不论微信&#xff0c;企业微信&#xff0c;千牛或其他手机APP回复客户&#xff0c;都可以用聊天宝APP实现图文一键发送&#xff0c;非常方便 前言 做客服工作&#xff0c;除了电脑上回复客户咨询&…

一文读懂Maven的安装与配置

一、前言【可忽略】 Maven本质是一个项目管理工具&#xff0c;类似于JDK是java开发工具。 我们需要管理什么呢&#xff1f;首先各种各样的依赖&#xff0c;比如SpringFramwork、Mybatis。 简单点做&#xff0c;我们新建个目录&#xff0c;就能管理这些jar包。然而&#xff0c;缺…

第 8 章 机器人平台设计之传感器(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 8.6.1 传感器_激光雷达简介 激光雷达是现今机器人尤其是无人车领域及最重要、最关键也是最常见的传感器之一&…

转型先锋!G7易流的数字化到底有多牛?

在供应链全球一体化进程中&#xff0c;国内外局势的改变&#xff0c;使得物流行业运力供大于求趋势愈加明显&#xff0c;国内供应链参与者面对内外发展需求和激烈的市场竞争&#xff0c;需要打破同质化竞争的局面&#xff0c;提供具有特色的服务&#xff0c;形成专业、高效、灵…

Hexo最新实战:(一)Hexo7.0+GitHub Pages博客搭建

前言 很多平台都能写博客还有创作激励&#xff0c;为什么我又要搭一个&#xff1f;为什么这次要选择用Hexo框架&#xff1f; 对应的原因是流量自由和省钱&#xff0c;第一个&#xff0c;很多平台能写但不是都有收益&#xff0c;而且平台有自身的规则&#xff0c;比如会屏蔽一…

2024第三届AIGC开发者大会圆桌论坛:AI Agent中国落地发展现状及多模态结合具身智能的发展展望

在2024年第三届AIGC开发者大会上&#xff0c;多位业内专家齐聚一堂&#xff0c;共同探讨了AI Agent在中国的落地发展现状以及多模态结合具身智能的发展前景。本次圆桌论坛的嘉宾包括&#xff1a; Fast JP作者于金龙Agent创始人莫西莫必胜作者秦瑞January Agent创始人李晨 多模…

C++编程函数中switch实例用法

switch语法 switch (func_cb.sta) switch后续跟随多个成对的case和break&#xff0c;分别包含if/endif判断语句 每个 case 后跟一个要比较的值和一个冒号&#xff0c;当被测试的变量等于 case 中的常量时&#xff0c;case下一行的语句将被执行 switch 语句可以嵌套。 嵌套时&am…

爬虫逆向实例小记——某数据知识管理网站-DES-ECB模式

aHR0cHM6Ly9rZC5uc2ZjLmNuL2ZpbmFsUHJvamVjdEluaXQ 注意&#xff1a;本文是逆向部分比较少&#xff0c;主要为了流程走通&#xff0c;限于代码搬运工。 第一步:分析页面 此网站经过请求响应&#xff0c;可以看出响应内容为加密内容。 第二步&#xff1a;判断加密类型 在XHR …

【Linux】解决误操作libc.so.6导致的问题,补充:升级glibc注意事项

千万不要轻易动/usr/lib64/libc.so.6。 glibc是Linux系统中最底层的api&#xff0c;Linux几乎所有运行库都依赖glibc。/usr/lib64/libc.so.6属于glibc&#xff0c;在centos7中是个软链接。 一旦误删或误操作libc.so.6&#xff0c;或者glibc新版本不兼容等原因&#xff0c;都可…

c++编程(13)——vector的模拟实现

欢迎来到博主的专栏——c编程 博主ID&#xff1a;代码小豪 文章目录 前言vector的模拟实现vector的成员对象插入、删除、扩容访问vector元素构造函数 填坑&#xff1a;为什么拷贝vector类元素的时候不能用浅拷贝末尾源代码&#xff1a; 前言 博主目前的水平还不能很明确的描述…

CV之Nougat:Nougat(一种基于神经网络实现OCR功能的视觉转换器模型)的简介、安装和使用方法、案例应用之详细攻略

CV之Nougat&#xff1a;Nougat(一种基于神经网络实现OCR功能的视觉转换器模型)的简介、安装和使用方法、案例应用之详细攻略 目录 相关论文 《Nougat: Neural Optical Understanding for Academic Documents》的翻译与解读 Nougat的简介 Nougat的安装和使用方法 1、安装 …

短视频拍摄方式有哪些:四川鑫悦里文化传媒有限公司

​短视频拍摄方式有哪些 在数字化时代&#xff0c;短视频以其短小精悍、传播迅速的特点&#xff0c;成为了人们表达自我、分享生活的重要工具。然而&#xff0c;想要制作出引人入胜的短视频&#xff0c;除了创意和构思&#xff0c;拍摄方式的选择也至关重要。四川鑫悦里文化传…

JavaEE:Servlet创建和使用及生命周期介绍

目录 ▐ Servlet概述 ▐ Servlet的创建和使用 ▐ Servlet中方法介绍 ▐ Servlet的生命周期 ▐ Servlet概述 • Servlet是Server Applet的简称&#xff0c;意思是 用Java编写的服务器端的程序&#xff0c;Servlet被部署在服务器中&#xff0c;而服务器负责管理并调用Servle…

香橙派KunpengPro测评之使用C语言操控40pin引脚

香橙派KunpengPro测评之使用C语言操控40pin引脚 香橙派KunpengPro介绍香橙派实物图香橙派登录界面香橙派KunpengPro的登录界面香橙派KunpengPro的原始桌面香橙派KunpengPro内安装了VScode等软件香橙派KunpengPro的终端 香橙派硬件参数核心性能图形与显示接口丰富性扩展与兼容性…

2024年中国金融行业网络安全研究报告

网络安全一直是国家安全的核心组成部分&#xff0c;特别是在金融行业&#xff0c;金融机构拥有大量的敏感数据&#xff0c;包括个人信息、交易记录、财务报告等&#xff0c;这些数据的安全直接关系到消费者的利益和金融市场的稳定&#xff0c;因此金融行业在网络安全建设领域一…

短道速滑短视频:四川京之华锦信息技术公司

短道速滑短视频&#xff1a;冰雪激情的视觉盛宴 随着冬奥会的热度不断攀升&#xff0c;短道速滑作为其中一项紧张刺激、充满观赏性的运动&#xff0c;受到了越来越多人的关注。而在社交媒体和短视频平台的助力下&#xff0c;短道速滑短视频成为了人们了解、欣赏这项运动的新窗…

MPLS原理与配置

1.MPLS概述 &#xff08;1&#xff09;传统IP路由转发 &#xff08;2&#xff09;MPLS基本概念 ⦁ MPLS起源于IPv4&#xff08;Internet Protocol version 4&#xff09;&#xff0c;其核心技术可扩展到多种网络协议&#xff0c;包括IPv6&#xff08;Internet Protocol ver…