喜咩咩和 Stream 的故事

一、背景提要

喜咩咩一家——喜咩咩、胡橘橘、大肥猫,因为胡橘橘和大肥猫日益肥胖,因而诞生了 “全家体重大普查计划” 。

二、第一次称重

喜咩咩家第一次称重开始啦,喜咩咩:100kg,胡橘橘:200kg,大肥猫:300kg;(在羊村100kg是标准体重!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class People {
String name;
int weight;

public People(String name, int weight) {
this.name = name;
this.weight = weight;
}

@Override
public String toString() {
return "People{" + "name='" + name + '\'' + ", weight=" + weight + '}';
}
}

我们先记录一下数据:

1
2
3
4
5
List<People> peoples = List.of(
new People("XYY", 100),
new People("HHR", 200),
new People("cat", 300)
);

我们想要直接先看到体重最重的是谁:

1
2
3
4
5
6
7
8
9
10
11
12
// max方法可以通过传入一个比较器求最大值
System.out.println(
peoples.stream().max((p1, p2) -> p1.weight - p2.weight).map(it -> it.name)
);
// 返回:Optional<cat>(最重的是大肥猫!!) 【注】Optional<cat>为可能为空的cat值

// 现在请思考一个问题,如果peoples数组为空,执行上面一条语句会怎样?
// 因此,为加强代码的可靠性,我们也可以采取下面的写法
peoples.stream()
.max((p1, p2) -> p1.weight - p2.weight)
.orElse(new People("none", -1))
.name;

然后对一家的体重进行降序排序:

1
2
// sorted可以实现排序功能
peoples = peoples.stream().sorted((p1, p2) -> p2.weight - p1.weight).toList();

并依次输出他们的名字:

1
2
3
// map是实现对元素本身的修改,在这里将每个元素映射修改为他的name属性
System.out.println(peoples.stream().map(p -> p.name).toList());
// 输出:[cat, HHR, XYY]

三、一个月后

经过喜咩咩仔细认真地主持, “全家体重大普查计划” 得到了顺利地执行,一个月的体重数据全部存储在数组 firstMonthData 中。

喜咩咩决定看看在这一个月中,体重超过 200kg 的分别有谁:

1
2
3
4
5
6
7
8
System.out.println(
firstMonthData.stream()
.filter(it -> it.weight > 200) // 使用filter方法,可以只留下满足条件的元素
.map(it -> it.name)
.distinct() // distinct方法,对流中的元素去重
.toList()
);
// 输出:[cat, HHR] (胡橘橘和大肥猫居然还是那么胖!)

喜咩咩决定看看这一个月来,胡橘橘和大肥猫的体重变化究竟是怎样的:

1
2
3
4
5
6
7
8
System.out.println(
firstMonthData.stream().filter(it -> "HHR".equals(it.name)).map(it -> it.weight).toList()
);
System.out.println(
firstMonthData.stream().filter(it -> "cat".equals(it.name)).map(it -> it.weight).toList()
);
// 输出:[200, 300, 400, 500]
// 输出:[300, 300, 300, 300]

喜咩咩黑人问号???这胡橘橘怎么不减反增??在喜咩咩的严刑拷打之下胡橘橘只能招供:胡橘橘在上个月每天都喝50瓶快乐水。喜咩咩心生一计,颁布喜咩咩家新政:胡橘橘和大肥猫比赛减肥,平均体重低的一方可以得到快乐水奖励。

四、又是一次 ”秋后算账“

胡橘橘在上个月痛改前非,视快乐水为粪土(实际上是觊觎最后的终极快乐水奖励),趁着大肥猫每次干饭、睡觉的时候,都出门跑步,跑五公里。这一次,胡橘橘非常自信地开始算自己的平均体重:

1
2
3
4
5
6
7
8
 System.out.println(
secondMonthData.stream()
.filter(it -> "HHR".equals(it.name))
.mapToInt(it -> it.weight) // 将对象映射为int
.average() // 可对流中的数据求平均值
.orElse(0)
);
// 输出:280.0

同时大肥猫的平均体重为 300kg,让我们恭喜胡橘橘获得胜利!奖品一箱快乐水,分期限量发放,每天一瓶。

五、请你查查大乌龙事件

“聪明”的胡橘橘心中暗波汹涌,他发现了一个规定盲区:既然比的是平均体重,那我何不在前半个月胡吃海喝满足自己,同时逃称;在后半个月积极减肥,上称减肥后的体重;那么这样算下来,我既可以享受半个月的食物,又可以拿到快乐水奖励。

于是胡橘橘就开始逃称。

很明显,第三个月的快乐水奖励又属于胡橘橘。但是大肥猫提出异议,他说胡橘橘经常在外和小姐姐彻夜不归,胡吃海喝,胡作非为,怎么可能一个月不长胖?大肥猫要求喜咩咩彻查此事。

喜咩咩在咨询了韩探长后,决定按照韩探长说的来,先查查有没有人逃称:

1
2
3
4
5
6
7
8
Map<String, Long> result = thirdMonthData.stream()
.collect( // 收集操作,需要传入一个收集器
Collectors.groupingBy( // 我们传入的groupingBy收集器
it -> it.name, Collectors.counting() // 传入收集器的收集方法,箭头后为Map对应的(key, value)
)
);
System.out.println(result);
// 输出:{HHR=3, cat=5, XYY=5}

嗯?胡橘橘竟然逃称了两次,没收一切快乐水,取消购买充电头资格两个月。

六、好转

很快,在喜咩咩的监督下,胡橘橘和大肥猫逐渐向健康体重发展。喜咩咩同学及时提出下一步规划:

  1. 在下个月只要胡橘橘和大肥猫任何一个人低于110kg就可以奖励全家一次KFC;
  2. 在下个月只要胡橘橘和大肥猫都低于180kg,就举家去上海旁的小海岛!
1
2
3
4
5
6
7
8
9
10
11
System.out.println(
forthMonthData.stream()
.filter(it -> !"XYY".equals(it.name))
.anyMatch(it -> it.weight <= 110) // anyMatch:流中任何一个元素满足lambda表达式则返回true
);
System.out.println(
forthMonthData.stream()
.allMatch(it -> it.weight <= 180) // allMatch:流中所有元素都满足lambda表达式则返回true
);
// 输出: true
// true

那么?下个月去上海旁边的小海岛吃KFC!(格局打开,去野炊吧!)

七、公示

最后,喜咩咩一家都拥有了健康体重,不会脂肪肝。全剧终。

喜咩咩 胡橘橘 大肥猫
100kg 110kg 120kg