《Java 8 实战》读书笔记(2)—Stream

一 前言

今天继续看第二部分,首先看了第四章,记录流的一些内容。
首先为什么要引入流。说一个场景:有一个菜单,要查找热量低的菜,并按热点大小排序。这个场景如果用sql很简单,但是在Java中用集合操作,以往的方法都是for循环处理,不知道你烦不烦,我每次处理这种需要循环挺烦的。尤其是嵌套循环。

二 流与集合

上面说的这个业务场景,我们一般的处理方式是这样的

# 定义一个菜单集合,存储数据
public class Dish {
    private final String name;
    private final boolean vegetarion;
    private final int calories;
    private final Type type;
    public enum Type{
        MEAT, FISH, OTHER
    }
    public Dish(String name, boolean vegetarion, int calories, Type type) {
        this.name = name;
        this.vegetarion = vegetarion;
        this.calories = calories;
        this.type = type;
    }
    public String getName() {
        return name;
    }
    public boolean isVegetarion() {
        return vegetarion;
    }
    public int getCalories() {
        return calories;
    }
    public Type getType() {
        return type;
    }
}
# 初始化集合数据
List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH));
# 用集合处理
# 1.先筛选
List<Dish> lowCaloricDishes = new ArrayList<>();
        for(Dish d: dishes){
            if(d.getCalories() < 400){
                lowCaloricDishes.add(d);
            }
        }
# 2.排序
List<String> lowCaloricDishesName = new ArrayList<>(); 
List<String> lowCaloricDishesName = new ArrayList<>();
        Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
            public int compare(Dish d1, Dish d2){
                return Integer.compare(d1.getCalories(), d2.getCalories());
            }
        });
# 3. 排序后生成新的菜单集合
 for(Dish d: lowCaloricDishes){
            lowCaloricDishesName.add(d.getName());
        }

流处理方式如下:

menu.stream()
                .filter(d -> d.getCalories() < 400)
                .sorted(comparing(Dish::getCalories))
                .map(Dish::getName)
                .collect(toList());

很显然,流式处理太简单了。

2.1 流简介

Java8 中流(接口定义在java.util.stream.Stream)的概念:
从支持数据处理操作的源生成的元素序列

  • 元素序列——和集合一样,有一组可访问的有序序列。
  • 源——流需要一个数据源,如集合,数组,文件等。
  • 数据处理操作——流的数据处理类似于SQL,以及函数式编程语言的操作。如Scala中的高阶算子等。

2.2 流与集合的区别

书中举了一个很好的例子,集合类似于存在DVD的中的电影(以字节或者帧的形式实际存储),流类似于在线看电影(按需计算所需内容,这里类似于云计算服务器租用的商业模式)。
集合是系统已经为对象分配了堆栈,核心是存储。流则是按需计算,核心是计算。

三 Java 8 中流的基本使用

书中有这样一个例子,对交易数据的分析。

## 定义一个交易人员类,存储交易人员数据
public  class Trader{
    
    private String name;
    private String city;

    public Trader(String n, String c){
        this.name = n;
        this.city = c;
    }

    public String getName(){
        return this.name;
    }

    public String getCity(){
        return this.city;
    }

    public void setCity(String newCity){
        this.city = newCity;
    }

    public String toString(){
        return "Trader:"+this.name + " in " + this.city;
    }
}
## 定义一个交易过程类
public class Transaction{

    private Trader trader;
    private int year;
    private int value;

    public Transaction(Trader trader, int year, int value)
    {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader(){ 
        return this.trader;
    }

    public int getYear(){
        return this.year;
    }

    public int getValue(){
        return this.value;
    }
    
    public String toString(){
        return "{" + this.trader + ", " +
               "year: "+this.year+", " +
               "value:" + this.value +"}";
    }
}
## 数据初始化
 Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario","Milan");
        Trader alan = new Trader("Alan","Cambridge");
        Trader brian = new Trader("Brian","Cambridge");
        
        List<Transaction> transactions = Arrays.asList(
            new Transaction(brian, 2011, 300), 
            new Transaction(raoul, 2012, 1000),
            new Transaction(raoul, 2011, 400),
            new Transaction(mario, 2012, 710),  
            new Transaction(mario, 2012, 700),
            new Transaction(alan, 2012, 950)

下面进行数据分析
(1)找出2011年发生的所有交易,并按交易额顺序排列(从低到高);
(2)交易员都在哪些不同的城市工作过;
(3)查找所有来自于剑桥的交易员,并按顺序排列;
(4)返回所有交易员的姓名字符串,并按字母顺序排序;
(5)有没有交易员在米兰工作?
(6)打印生活在剑桥的交易员的所有交易额;
(7)所有交易中,最高的交易额是多少?
(8)找到交易额最小的交易。

# (1)找出2011年发生的所有交易,并按交易额顺序排列(从低到高)
List< Transaction > tr2011=transactions.stream.filter(x->x.getYear()==2011)
                                .sorted((a,b)->a. getValue().compareTo(b.getVaule())
                                 .collect(toList());
# (2) 交易员都在哪些不同的城市工作过
List<String> cities=transactions.stream.map(x->x.getTrader().getName())
                                .distinct()
                                .collect(toList());
# (3)查找所有来自于剑桥的交易员,并按顺序排列
List<Trader> traders= transactions.stream.map(x->x.getTrader())
                               .filter(x->x.getCity ().equlas("Cambridge"))
                               .distinct()
                               .sorted(comparing(Trader::getName()))
                               .collect(toList());
# (4)返回所有交易员的姓名字符串,并按字母顺序排序
String names=transactions.stream.map(x->x.getTrader().getName())
                                .distinct()
                                .reduce("",(x,y)->x+y) ;
# (5)有没有交易员在米兰工作?
bolean ismilan=transactions.stream.anyMatch(x->x.getTrader().getCity ().equlas("Milan"));
# (6) 打印生活在剑桥的交易员的所有交易额
transactions.stream .filter(x->x.getTrader().getCity().
                                equlas("Cambridge"))
                               .forEach(x->System.out.println(x.getValue());
# (7)所有交易中,最高的交易额是多少?
Option<Integer> highValue=transactions.stream .map(x- >x.getValue()) .reduce(Integer:max);
# (8)找到交易额最小的交易。
int min=transactions.stream.min(comparing(Transaction::getValue()))

四 数值流的使用

Java的数据类型有引用类型和值类型两种,而泛型只能绑定引用类型,这样就存在装箱和拆箱的过程(这个过程是有性能消耗的),所以Java8 Stream定义了IntStream、LongStream和DoubleStream等数值流。

## 对象流映射数值流,maptoInt,maptoDouble,maptoLong,
## menu对象在前面定义了。例如
IntStream intstream=menu.stream().maptoInt(Dish::getValue);

## 数值流转换对象流
Stream<Integer> stream=intstream.boxed();

## 默认值OptionalInt
## Optional是为了处理空值或者不存在值。

例子:计算1到100之间自然数的勾股数,即a * a+b * b=c * c;

## 1.定义a,取值范围为[1,100]
IntStream a1=IntStream.rangeClosed(1,100);
## 2.转换为对象流
Stream<Integer> a2=a1.boxed();
## 3.把每个a的值映射三元数流
a2.flatMap(a->{
       //定义b值,范围为[a,100]
       IntStream b1 =IntStream.rangeClosed(a,100);
       //判断a*a+b*b的平方根是否为整数
       IntStream b2=b1.filter(b->Math.sqrt(a*a+b*b)%1==0);
       // 转换a2为数值流
      b2.mapToObj(b->new int[]{a,b,(int)Math.sqrt(a*a+b*b)})
})
//打印前5组勾股数
a1.limit(5).forEach(t->System.out.println(t[0]+t[1]+t[2]));

五 无限流的使用

Stream提供了两个静态方法来从函数生成流:Stream.iterate和Stream.generate。
斐波那契数列生成

Stream.iterate(new int[]{0,1},t->new int[]{t(1),t(0)+t(1)})
            .limit(10)
            .map(t->t[0])
            .forEach(System.out::println);
## 打印结果
0,1,1,2,3,5,8,13,21,34...

参考文章

[1]《Java 8实战》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容