当前位置 > it书童 > java > 正文

Stream 实战案例

java it书童 2021-01-14 16:22:40 0赞 0踩 171阅读 0评论

案例1

班级中有20名学生,每名学生有5门课的考试成绩,其中缺考的科目分数字段为空。需要找出有缺考的学生叫什么名字

package stream.cases;

import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CaseOne {
    @Data
    @AllArgsConstructor
    class ExamStudentScore {
        private String studentName;
        private Integer scoreValue;
        private String subject;
    }

    // 学生考试成绩
    Map<String, List<ExamStudentScore>> studentMap;

    @Before
    public void init() {
        studentMap = new HashMap<>();

        List<ExamStudentScore> zsScoreList = new ArrayList<>();
        zsScoreList.add(
            new ExamStudentScore(
                "张三",
                30,
                "CHINESE"));
        zsScoreList.add(
            new ExamStudentScore(
                "张三",
                40,
                "ENGLISH"));
        zsScoreList.add(
            new ExamStudentScore(
                "张三",
                50,
                "MATHS"));
        studentMap.put("张三", zsScoreList);

        List<ExamStudentScore> lsScoreList = new ArrayList<>();
        lsScoreList.add(
            new ExamStudentScore(
                "李四",
                80,
                "CHINESE"));
        lsScoreList.add(
            new ExamStudentScore(
                "李四",
                null,
                "ENGLISH"));
        lsScoreList.add(
            new ExamStudentScore(
                "李四",
                100,
                "MATHS"));
        studentMap.put("李四", lsScoreList);

        List<ExamStudentScore> wwScoreList = new ArrayList<>();
        wwScoreList.add(
            new ExamStudentScore(
                "王五",
                null,
                "CHINESE"));
        wwScoreList.add(
            new ExamStudentScore(
                "王五",
                null,
                "ENGLISH"));
        wwScoreList.add(
            new ExamStudentScore(
                "王五",
                70,
                "MATHS"));
        studentMap.put("王五", wwScoreList);
    }

    @Test
    public void findStudent() {
        studentMap.forEach((studentName, scoreList) -> {
            boolean bool = scoreList.stream()
                .anyMatch(score -> {
                    return score.getScoreValue() == null;
                });
            if (bool) {
                System.out.println("此学生[" +  studentName + "]有缺考情况");
            }
        });
    }
}

结果:

此学生[李四]有缺考情况
此学生[王五]有缺考情况

案例2

标签管理功能模块:允许用户批量添加标签,后台需要对标签去重,并且需要防止数据库中存在同名的标签

package stream.cases;

import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

public class CaseTwo {
    /**
     * 用户请求的创建标签模型
     */
    @Data
    @AllArgsConstructor
    class TagReqDTO {
        private String name;
        private Integer age;
    }

    /**
     * 从DB中查询出来的已经存在的标签名
     */
    List<String> tagListFromDB;
    
    /**
     * 用户请求的标签列表
     */
    List<TagReqDTO> tagListFromReq;

    @Before
    public void init() {
        // 数据库中存在的标签名列表
        tagListFromDB = Lists.newArrayList(
            "李四", "王五", "赵六");

        // 用户提交的
        tagListFromReq = Lists.newArrayList(
            new TagReqDTO("张三", 10),
            new TagReqDTO("李四", 30),
            new TagReqDTO("张三", 10));
    }

    @Test
    public void distinctTag() {
        tagListFromReq.stream()
            // true:通过测试,数据不过滤;false:未通过测试,数据被过滤
            .filter(tag -> !tagListFromDB.contains(tag.getName()))
            // 使用equals对元素进行比较
            .distinct()
            .forEach(System.out::println);
    }
}

结果:

CaseTwo.TagReqDTO(name=张三, age=10)

案例3

权限管理功能模块:查询某用户所有角色下所包含的权限名称

package stream.cases;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class CaseThree {
    /**
     * 角色
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    class Role {
        // 权限列表
        private List<Permission> permissions;
    }

    /**
     * 权限
     */
    @Data
    @AllArgsConstructor
    class Permission {
        // 权限名称
        private String name;
    }

    // 用户角色列表
    List<Role> roleList;

    @Before
    public void init() {
        roleList = new ArrayList();

        Role adminRole = new Role();
        List<Permission> adminPermissionList = Lists.newArrayList(
            new Permission("删除"),
            new Permission("查看"),
            new Permission("导出"));
        adminRole.setPermissions(adminPermissionList);

        Role userRole = new Role();
        List<Permission> userPermissionList = Lists.newArrayList(
            new Permission("新建"),
            new Permission("修改"),
            new Permission("删除"),
            new Permission("查看"));
        userRole.setPermissions(userPermissionList);

        roleList.add(adminRole);
        roleList.add(userRole);
    }

    @Test
    public void findPermission() {
        List<Permission> collect = roleList.stream()
            // 扁平化MAP 获取对象中的集合类属性,组成一个新的流
            .flatMap(role -> role.getPermissions().stream())
            // peek 与 forEach 类似,区别是用在中间过程中,后面可以接其他操作
            .distinct()
            .collect(Collectors.toList());
        System.out.println(JSON.toJSONString(collect, true));
    }
}

结果:

[
	{
		"name":"删除"
	},
	{
		"name":"查看"
	},
	{
		"name":"导出"
	},
	{
		"name":"新建"
	},
	{
		"name":"修改"
	}
]

案例4

设计一个对外提供服务的接口,支持调用方传入多个账户编号查询订单

package stream.cases;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CaseFour {
    @Data
    @AllArgsConstructor
    class Order {
        private Integer orderId;
        private String accountId;
    }

    /**
     * 模拟数据库查询
     *
     * @param accountIds
     * @return
     */
    public List<Order> selectFromDB(List<String> accountIds) {
        List<Order> orderList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            orderList.add(
                new Order(i,
                    accountIds.get(i % accountIds.size())));
        }
        return orderList;
    }

    /**
     * 接口
     *
     * @param accountIds
     * @return
     */
    public Map<String, List<Order>> queryOrderByAccountIds(
        List<String> accountIds) {
        return Optional.ofNullable(selectFromDB(accountIds))
            .map(List::stream)
            .orElseGet(Stream::empty)
            // group分组功能
            .collect(Collectors.groupingBy(
                Order::getAccountId)
            );
    }

    @Test
    public void test() {
        Map<String, List<Order>> orders = queryOrderByAccountIds(
            Lists.newArrayList("张三", "李四", "王五")
        );

        System.out.println(JSON.toJSONString(orders, true));
    }
}

结果:

{
	"李四":[
		{
			"accountId":"李四",
			"orderId":1
		},
		{
			"accountId":"李四",
			"orderId":4
		},
		{
			"accountId":"李四",
			"orderId":7
		}
	],
	"张三":[
		{
			"accountId":"张三",
			"orderId":0
		},
		{
			"accountId":"张三",
			"orderId":3
		},
		{
			"accountId":"张三",
			"orderId":6
		},
		{
			"accountId":"张三",
			"orderId":9
		}
	],
	"王五":[
		{
			"accountId":"王五",
			"orderId":2
		},
		{
			"accountId":"王五",
			"orderId":5
		},
		{
			"accountId":"王五",
			"orderId":8
		}
	]
}

案例5

在股票中,撮合交易的原则是一段时间内的交易申请,价格越高的先成交;价格一样,下单时间最早的先成交;价格和时间一致,交易量大的先成交;如何价格、时间和交易量都一致,机构优先成交,散户最后成交。

现有一批交易申请数据,需要确认交易先后顺序。

package stream.cases;

import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.Before;
import org.junit.Test;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class CaseFive {
    /**
     * 交易实体模型
     */
    @Data
    @AllArgsConstructor
    static class Trade {
        // 下单价格
        private BigDecimal price;
        // 下单时间
        private LocalDateTime time;
        // 下单量
        private Integer count;
        // 下单类型:机构 / 个人
        private String type;
    }

    /**
     * 一段时间内的交易申请
     */
    List<Trade> trades;

    @Before
    public void init() {
        trades = new ArrayList<>();

        trades.add(new Trade(new BigDecimal(100),
            // 在当前时间的基础上添加 1 秒
            LocalDateTime.now().plusSeconds(1),
            500, "机构"));
        trades.add(new Trade(new BigDecimal(101),
            LocalDateTime.now().plusSeconds(2),
            1, "个人"));
        trades.add(new Trade(new BigDecimal(101),
            LocalDateTime.now().plusSeconds(1),
            1, "个人"));
        trades.add(new Trade(new BigDecimal(100),
            LocalDateTime.now().plusSeconds(1),
            500, "个人"));
        trades.add(new Trade(new BigDecimal(100),
            LocalDateTime.now().plusSeconds(0),
            2, "个人"));
        trades.add(new Trade(new BigDecimal(100),
            LocalDateTime.now().plusSeconds(0),
            100, "机构"));
    }

    @Test
    public void sortTrade() {
        System.out.println("排序前数据~~~\n" + JSON.toJSONString(trades, true));

        List<Trade> sorted = trades.stream()
            .sorted(
                Comparator
                    // 首先按照价格排序
                    .comparing(
                        Trade::getPrice,
                        // 进行排序调整,将自然排序翻转
                        Comparator.reverseOrder())

                    // 时间先后进行排序,自然排序
                    .thenComparing(Trade::getTime)

                    // 交易量排序,自然排序翻转
                    .thenComparing(
                        Trade::getCount,
                        Comparator.reverseOrder())

                    // 自定义排序规则
                    .thenComparing(
                        // 要排序的字段值
                        Trade::getType,

                        // 自定义排序规则
                        (type1, type2) -> {
                            if ("机构".equals(type1) && "个人".equals(type2)) {
                                // -1:type1在先, type2在后
                                return -1;
                            } else if ("个人".equals(type1) && "机构".equals(type2)) {
                                return 1;
                            } else {
                                return 0;
                            }
                        }))
            .collect(Collectors.toList());

        System.out.println("排序后结果~~~\n" + JSON.toJSONString(sorted, true));
    }
}

结果:

排序前数据~~~
[
	{
		"count":500,
		"price":100,
		"time":"2021-01-10T18:24:34.330",
		"type":"机构"
	},
	{
		"count":1,
		"price":101,
		"time":"2021-01-10T18:24:35.330",
		"type":"个人"
	},
	{
		"count":1,
		"price":101,
		"time":"2021-01-10T18:24:34.330",
		"type":"个人"
	},
	{
		"count":500,
		"price":100,
		"time":"2021-01-10T18:24:34.330",
		"type":"个人"
	},
	{
		"count":2,
		"price":100,
		"time":"2021-01-10T18:24:33.330",
		"type":"个人"
	},
	{
		"count":100,
		"price":100,
		"time":"2021-01-10T18:24:33.330",
		"type":"机构"
	}
]
排序后结果~~~
[
	{
		"count":1,
		"price":101,
		"time":"2021-01-10T18:24:34.330",
		"type":"个人"
	},
	{
		"count":1,
		"price":101,
		"time":"2021-01-10T18:24:35.330",
		"type":"个人"
	},
	{
		"count":100,
		"price":100,
		"time":"2021-01-10T18:24:33.330",
		"type":"机构"
	},
	{
		"count":2,
		"price":100,
		"time":"2021-01-10T18:24:33.330",
		"type":"个人"
	},
	{
		"count":500,
		"price":100,
		"time":"2021-01-10T18:24:34.330",
		"type":"机构"
	},
	{
		"count":500,
		"price":100,
		"time":"2021-01-10T18:24:34.330",
		"type":"个人"
	}
]
关于我
一个文科出身的程序员,追求做个有趣的人,传播有价值的知识,微信公众号主要分享读书思考心得,不会有代码类文章,非程序员的同学请放心订阅
转载须注明出处:https://www.itshutong.com/articles/1028