Java-8-流(1)

Java-8-流(1)

外部迭代与内部迭代

Java 程序员在使用集合类时,一个通用的模式是在集合上进行迭代,然后处理返回的每一
个元素

在数字集合里面统计大于100的数有几个

  public static void main(String[] args) {

        List data = Number_Data.createData();

        int count = 0;

        for (Integer integer : data){

            if (integer > 100){
                count++;
            }
        }

        System.out.println(count);
    }

尽管这样的操作可行,但存在几个问题。每次迭代集合类时,都需要写很多样板代码。将for 循环改造成并行方式运行也很麻烦,需要修改每个 for 循环才能实现

for 循环的样板代码模糊了代码的本意,程序员必须阅读整个循环体才能理解。若是单一的 for 循环,倒也问题不大,但面对一个满是循环(尤其是嵌套循环)的庞大代码库时,负担就重了

for 循环其实是一个封装了迭代的语法糖看看它的工作原理。首先调用 iterator 方法,产生一个新的 Iterator 对象,进而控制整个迭代过程,这就是外部迭代。迭代过程通过显式调用 Iterator 对象的 hasNext 和 next方法完成迭代

使用迭代器在数字集合里面统计大于100的数有几个


        int num = 0;

        Iterator integerIterator = data.iterator();

        while (integerIterator.hasNext()){

            if (integerIterator.next() > 100){
                num++;
            }

        }

        System.out.println(num);

简单理解外部迭代就是由用户来决定”做什么“和”怎么做“的操作

另一种方法就是内部迭代

使用内部迭在数字集合里面统计大于100的数有几个


   List data = Number_Data.createData();

        long count = data.stream()
                .filter(integer -> integer > 100)
                .count();

        System.out.println(count);

每种操作都对应 Stream 接口的一个方法。为了找出来大于100的数有几个,需要对 Stream 对象进行过滤: filter 。过滤在这里是指“只保留通过某项测试的对象”。测试由一个函数完成,根据数字是否大于100,该函数返回 true 或者 false 。由于 Stream API 的函数式编程风格,我们并没有改变集合的内容,而是描述出 Stream 里的内容。 count() 方法计算给定 Stream 里包含多少个对象

内部迭代我们只需要提供”做什么“,把”怎么做“的任务交给了 JVM

使用内部迭代可以带来的好处:

  • 用户只需要关注问题,无需关注如何解决问题的细节
  • 使得 JVM可以利用短路、并行等对性能的提升变成可能

Stream

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。

  • parallelStream() − 为集合创建并行流。

流的操作类型

  • Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历
  • Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

在对于一个 Stream 进行多次转换操作 (Intermediate 操作),每次都对 Stream 的每个元素进行转换,而且是执行多次,这样时间复杂度就是 N(转换次数)个 for 循环里把所有操作都做掉的总和吗?

其实不是这样的,转换操作都是 lazy 的,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream 里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在 Terminal 操作的时候循环 Stream 对应的集合,然后对每个元素执行所有的函数。

流的构造与转换


public class M3 {


    public static void main(String[] args) {

        Stream stream1;

        Stream stream2;

        Stream stream3;
        //由单独的值构成
        Stream stringStream = Stream.of("sjd","kjfu","4545");

        //由数组构成
        String[] strings = new String[]{"a","ab","abc"};

        stream1 = Stream.of(strings);

        stream2 = Arrays.stream(strings);

//        //由集合构成,最常用了
        List list = Arrays.asList(strings);

        stream3 = list.stream();

//对于基本数值型,目前有三种对应的包装类型的Stream:IntStream、LongStream、DoubleStream
        IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
        System.out.println("========================================");
        IntStream.range(1, 3).forEach(System.out::println);
        System.out.println("========================================");
        IntStream.rangeClosed(1, 3).forEach(System.out::println);



    }
}


自己构造流

Stream.generate

Stream.generate通过实现 Supplier 接口,你可以自己来控制流的生成


 public static void main(String[] args) {


        //      //生成100以内的30个随机整数,用来构造测试随机数不失为一种简便的方式
        Stream.generate(() ->
                new Random().nextInt(100))
                .limit(30).forEach(System.out::println);


        System.out.println("=================================");
        IntStream.generate(() ->
                (int) (System.nanoTime() % 100)).limit(20).forEach(System.out::println);



        System.out.println("=================================");

//                //random其实提供了更方便的ints()方法
        new Random().ints().limit(10).forEach(System.out::println);
    }

Stream.generate() 还接受自己实现的 Supplier。例如在构造海量测试数据的时候,用某种自动的规则给每一个变量赋值

public class PersonDto {

    private String name;

    private int age;

    private double height;


    public PersonDto(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        this.height = height;
    }
}





public class PersonSupplier implements Supplier {


    private int index = 0;

    private Random random = new Random();

    @Override
    public PersonDto get() {
        return new PersonDto("lin" + index,index++,random.nextDouble());
    }


    public static void main(String[] args) {

        Stream.generate(new PersonSupplier())
                .limit(15)
                .forEach( personDto ->
                        System.out.println(personDto.getName()+"  "
                        + personDto.getAge()+"   " +
                                personDto.getHeight()));

    }
}


Stream.iterate


public class M5 {


    public static void main(String[] args) {


//        iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。在 iterate 时候管道必须有 limit 这样的操作来限制 Stream 大小。

        Stream.iterate(0,n -> n+3).limit(10).forEach(x ->
                System.out.println(x + "  "));

    }
}

流可以转换为数组、集合

public class M6 {

    public static void main(String[] args) {

        Stream stringStream = Stream.of("one", "two", "three", "four");

        String[] strings_1 = stringStream.toArray(String[]::new);

        System.out.println(Arrays.toString(strings_1));

        System.out.println("==================================");


        Stream stringStream_1 = Stream.of("one", "two", "three", "four");

        List list_1 = stringStream_1.collect(Collectors.toList());

        System.out.println(list_1);

        System.out.println("==================================");

        Stream stringStream_2 = Stream.of("one", "two", "three", "four");


        List list_2 = stringStream_2.collect(Collectors.toCollection(ArrayList::new));

        System.out.println(list_2);

        System.out.println("==================================");


        Stream stringStream_3 = Stream.of("one", "two", "three", "four");


        Set set_1 = stringStream_3.collect(Collectors.toSet());


        System.out.println(set_1);

        System.out.println("==================================");

//        Stack stack1 = stringStream.collect(Collectors.toCollection(Stack::new));

//        System.out.println(stack1);

        System.out.println("==================================");
        

    }
}


常用的流操作

collect

Collectors.toList():转换成List集合


    public static void main(String[] args) {

        List list = Stream.of("1","2","3")
                .collect(Collectors.toList());

        System.out.println(list);


        Set set = Stream.of(1,2,3)
                .collect(Collectors.toSet());

        System.out.println(set);
    }

Collectors.toCollection(TreeSet::new):转换成特定的set集合



    public static void main(String[] args) {


        TreeSet treeSet = Stream.of(1,2,3,4,5)
                .collect(Collectors.toCollection(TreeSet::new));

        System.out.println(treeSet);


        HashSet hashSet = Stream.of(1,2,3,4,5)
                .collect(Collectors.toCollection(HashSet::new));


        System.out.println(hashSet);


    }

Collectors.toMap(keyMapper, valueMapper, mergeFunction):转换成map



    public static void main(String[] args) {

        Map map = Stream.of("a","b","c")
                .collect(Collectors.toMap(
                        x -> x,
                        x -> x+x,
                        (old,newval) -> newval
                ));


        map.forEach(
                (k,v) ->
                        System.out.println(k + "---" + v)
        );


    }

Collectors.minBy(Integer::compare):求最小值


    public static void main(String[] args) {

        Optional min = Stream.of(1,2,3,4,5,6)
                .collect(Collectors.minBy(Integer::compare));


        System.out.println(min.get());


    }

Collectors.averagingInt(x->x):求平均值


    public static void main(String[] args) {

        double average = Stream.of(1,2,3,4,5)
                .collect(Collectors.averagingInt(x->x));

        System.out.println(average);
    }


Collectors.summingInt(x -> x)):求和


    public static void main(String[] args) {

        int sum = Stream.of(1,2,3,4,5,6,7,8,9,10)
                .collect(Collectors.summingInt(x->x));

        System.out.println(sum);


    }

Collectors.summarizingDouble(x -> x):可以获取最大值、最小值、平均值、总和值、总数



    public static void main(String[] args) {

        DoubleSummaryStatistics summaryStatistics = Stream.of(1,2,3,4,5,6)
                .collect(Collectors.summarizingDouble(x->x));

        System.out.println(summaryStatistics.getMax());

        System.out.println(summaryStatistics.getAverage());

        System.out.println(summaryStatistics.getMin());

        System.out.println(summaryStatistics.getSum());

    }

counting:Stream的元素个数


 public static void main(String[] args) {


        System.out.println(Stream.of(1,2,3,4,5,6).collect(Collectors.counting()));

        

    }

分组groupingBy

groupingBy()是Stream API中最强大的收集器Collector之一,提供与SQL的GROUP BY子句类似的功能。

使用形式如下:


.collect(groupingBy(...));


按长度对字符串进行分组

public class M4 {


    public static void main(String[] args) {


        List strings = Arrays.asList(
                "a",
                "bb",
                "ccc",
                "dddd",
                "eeeee"
        );

        Map> map =

                strings.stream()
                .collect(
                        Collectors.groupingBy(String::length)
                );


        System.out.println(map);



    }
}




如果需要提供自定义Map实现,可以使用提供的groupingBy()重载来实现

public class M5 {


    public static void main(String[] args) {

        List strings = Arrays.asList(
                "a",
                "bb",
                "ccc",
                "dddd",
                "eeeee"
        );

        TreeMap> map =

                strings.stream()
                .collect(
                        Collectors.groupingBy(
                                String::length,
                                TreeMap::new,
                                Collectors.toList()
                        )
                );

        System.out.println(map);


    }
}


分组计数


 public static void main(String[] args) {

        List strings = Arrays.asList(
                "a",
                "bb",
                "ccc",
                "dddd",
                "eeeee"
        );


        Map map = strings.stream()
                .collect(
                        Collectors.groupingBy(
                                String::length,
                                Collectors.counting()
                        )
                );

        System.out.println(map);

    }

User类


public class User {

    private String name;

    private String origin;

    private int age;

    public User(String name, String origin, int age) {
        this.name = name;
        this.origin = origin;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getOrigin() {
        return origin;
    }

    public void setOrigin(String origin) {
        this.origin = origin;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", origin='" + origin + '\'' +
                ", age=" + age +
                '}';
    }
}



    public static void main(String[] args) {

        List users = User_Data.create();


//        统计每个地方的人各有多少
        Map c1 = users.stream().collect(
                Collectors.groupingBy(
                        User::getOrigin,Collectors.counting()
                )
        );



//{nanjing=5, shanghai=4, guangzhou=4, shenzhen=3, beijing=4}
        System.out.println(c1);

    }
多个属性拼接出一个组合属性


public class M2 {


    private static String myM(User user){
        return user.getName()+ "  from  " + user.getOrigin();
    }


    public static void main(String[] args) {

//多个属性拼接出一个组合属性
        List users = User_Data.create();

        Map> map = users.stream().collect(
                Collectors.groupingBy(
                        e->myM(e)
                )
        );

        for (String s : map.keySet()){
            System.out.println(s);
        }

//        System.out.println(map);

    }
}




    public static void main(String[] args) {

        Map> map = Stream.of(
                1,2,3,4,5,6
        ).collect(
                Collectors.partitioningBy(x -> x>2)
        );

        System.out.println(map);

        System.out.println("---------------------------------------------");

        Map longMap = Stream.of(1, 3, 3, 2).collect(Collectors.partitioningBy(x -> x > 1, Collectors.counting()));
        
        System.out.println(longMap);
        
        //{false=[1, 2], true=[3, 4, 5, 6]}
        //---------------------------------------------
        //{false=1, true=3}
        
        

    }

分组和计算每组的总和

派生每组条目的平均属性,那么有一些方便的收集器:

  • averagingInt()

  • averagingLong()

  • averagingDouble()


 public static void main(String[] args) {

        List strings = Arrays.asList(
                "1",
                "22",
                "333",
                "4444",
                "55555"
        );

        Map res = strings.stream()
                .collect(
                        Collectors.groupingBy(
                                String::length,
                                Collectors.averagingInt(String::hashCode)
                        )
                );

        //{1=49.0, 2=1600.0, 3=50643.0, 4=1600768.0, 5=5.0578165E7}
        System.out.println(res);
        
    }

分组和计算每组的总和

对分组条目进行累计总和:

  • summingInt()

  • summingLong()

  • summingDouble()


  public static void main(String[] args) {

        List strings = Arrays.asList(
                "1",
                "22",
                "333",
                "4444",
                "55555",
                "aa",
                "aa",
                "aaa"
        );


        Map map = strings.stream()
                .collect(
                        Collectors.groupingBy(
                                String::length,
                                Collectors.summingInt(String::length)
                        )
                );

//{1=1, 2=6, 3=6, 4=4, 5=5}
        System.out.println(map);

    }

计算最大最小值


    public static void main(String[] args) {

        List strings = Arrays.asList(
                "a",
                "bb",
                "ccc",
                "dddd",
                "eeeee"
        );

        Map> map =

                strings.stream()
                .collect(
                        Collectors.groupingBy(
                                String::length,
                                Collectors.maxBy(
                                        Comparator.comparing(String::toUpperCase)
                                )
                        )
                );

//{1=Optional[a], 2=Optional[bb], 3=Optional[ccc],
// 4=Optional[dddd], 5=Optional[eeeee]}
        System.out.println(map);

    }


public class User {

    private String name;
    private int price;
    private BigDecimal salary;


    public User(String name, int price, BigDecimal salary) {
        this.name = name;
        this.price = price;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }


    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", price=" + price +
                ", salary=" + salary +
                '}';
    }
}



    public static void main(String[] args) {

        List items = Arrays.asList(
                new User("apple", 10, new BigDecimal("9.99")),
                new User("banana", 20, new BigDecimal("19.99")),
                new User("orang", 10, new BigDecimal("29.99")),
                new User("watermelon", 10, new BigDecimal("29.99")),
                new User("papaya", 20, new BigDecimal("9.99")),
                new User("apple", 10, new BigDecimal("9.99")),
                new User("banana", 10, new BigDecimal("19.99")),
                new User("apple", 20, new BigDecimal("9.99"))
        );

//       // 计算名字出现的次数
        Map counting = items.stream().collect(
                Collectors.groupingBy(User::getName,Collectors.counting())
        );


        System.out.println(counting);


//        /       // 计算每个人金额是多少

        Map sum = items.stream().collect(
                Collectors.groupingBy(User::getName,Collectors.summingInt(User::getPrice))
        );

        System.out.println(sum);


//        /group by Salary
        Map> groupByPriceMap =
                items.stream().collect(Collectors.groupingBy(User::getSalary));

        System.out.println(groupByPriceMap);


        // group by Salary, uses 'mapping' to convert List to Set
        Map> result =
                items.stream().collect(
                        Collectors.groupingBy(User::getSalary,
                                Collectors.mapping(User::getName, Collectors.toSet())
                        )
                );
        System.out.println(result);

    }
}

输出:


{papaya=1, banana=2, apple=3, orang=1, watermelon=1}



{papaya=20, banana=30, apple=40, orang=10, watermelon=10}



{
    19.99=[User{name='banana', price=20, salary=19.99}, User{name='banana', price=10, salary=19.99}], 

    29.99=[User{name='orang', price=10, salary=29.99}, User{name='watermelon', price=10, salary=29.99}], 

    9.99=[User{name='apple', price=10, salary=9.99}, User{name='papaya', price=20, salary=9.99}, User{name='apple', price=10, salary=9.99}, User{name='apple', price=20, salary=9.99}]
}


{
    19.99=[banana], 
    29.99=[orang, watermelon], 
    9.99=[papaya, apple]
}

Collectors.partitioningBy(x -> x > 2)

在JDK8中,可以对流进行方便的自定义分块,通常是根据某种过滤条件将流一分为二

partitioningBy函数的定义如下:


public static 
    Collector>> partitioningBy(Predicate predicate) 

函数的参数一个Predicate接口,那么这个接口的返回值是boolean类型的,也只能是boolean类型,然后他的返回值是Map的key是boolean类型,也就是这个函数的返回值只能将数据分为两组也就是ture和false两组数据

例子:



    public static void main(String[] args) {

        // 创建一个包含人名称的流(英文名和中文名)
        Stream stream = Stream.of("Alen", "Hebe", "Zebe", "张成瑶", "钟其林");

        // 通过判断人名称的首字母是否为英文字母,将其分为两个不同流

        final Map> map =

                stream.collect(
                        Collectors.partitioningBy(
                                s ->
                                {
                                    int code = s.codePointAt(0);
                                    return (code>=65&&code<=90) ||
                                            (code>=97&&code<=122);
                                }
                        )
                );

//        / 输出分组结果

        map.forEach((isEnglishName, names) -> {
            if (isEnglishName) {
                System.out.println("英文名称如下:");
            } else {
                System.out.println("中文名称如下:");
            }
            names.forEach(name -> System.out.println("\t" + name));
        });


        //中文名称如下:
        //  张成瑶
        //  钟其林
        //英文名称如下:
        //  Alen
        //  Hebe
        //  Zebe

    }

把随机数字根据是否大于150分成两组
public static void main(String[] args) {

        List d = NumData.create();

        Map> map =

                d.stream()
                .collect(
                        Collectors.partitioningBy(integer -> integer>=150)
                );

        System.out.println(map);

//        {
//        false=[75, 63, 129, 86, 1, 38, 12, 90, 130, 41, 30, 40, 141, 106, 111, 55],
//        true=[173, 151, 226, 183, 157, 150, 150, 200, 192, 211, 222, 187, 230, 210]
//        }


    }

Collectors.joining(","):拼接字符串

在JDK8中,可以采用函数式编程(使用 Collectors.joining 收集器)的方式对字符串进行更优雅的连接。Collectors.joining 收集器 支持灵活的参数配置,可以指定字符串连接时的 分隔符,前缀 和 后缀 字符串


   public static void main(String[] args) {

        final String[] names = {"Zebe", "Hebe", "Mary", "July", "David"};
        Stream stream1 = Stream.of(names);
        Stream stream2 = Stream.of(names);
        Stream stream3 = Stream.of(names);

        String r1 = stream1.collect(
                Collectors.joining(",","[","]")
        );

        System.out.println(r1);

        System.out.println("---------------------------");

        String r2 = stream2.collect(
                Collectors.joining(" |","","")
        );

        System.out.println(r2);

        System.out.println("---------------------------");

        String r3 = stream3.collect(
                Collectors.joining(" -> ","","")
        );

        System.out.println(r3);

        //[Zebe,Hebe,Mary,July,David]
        //---------------------------
        //Zebe |Hebe |Mary |July |David
        //---------------------------
        //Zebe -> Hebe -> Mary -> July -> Davi

    }

Collectors.mapping(...)

可以把收集到的数据传递给 Collectors.mapping() 方法进行映射处理,以获取一些特定的信息mapping() 方法接收两个参数,第一个参数为如何处理每一条数据,第二个参数为当 mapping 完成后如何处理数据

例子:

public class User_Data {


    static Random random = new Random(55);

    static String[] names = new String[]
            {

                    "张",
                    "刘",
                    "陈",
                    "王",
                    "许"
            };

    public static List create(){

        List users = new ArrayList<>();

        for (int i = 0; i < 30; i++) {

            users.add(new User(names[random.nextInt(names.length)]+i,random.nextInt(100)));

        }

        return users;

    }
}



    public static void main(String[] args) {

        List users = User_Data.create();

        List names =

                users.stream()
                .collect(Collectors.mapping(
                        User::getName,Collectors.toList()
                ));

        //[张0, 许1, 陈2, 王3, 许4, 刘5, 张6, 刘7, 刘8, 陈9, 陈10, 张11,
        // 刘12, 陈13, 许14, 许15, 王16, 许17, 王18, 张19, 刘20, 许21, 刘22,
        // 张23, 张24, 刘25, 陈26, 许27, 刘28, 陈29]
        System.out.println(names);

    }


Map> collect = users.stream().collect(Collectors.groupingBy(User::getAge, Collectors.mapping(item ->{

            //当然你这里也可以构建一个新的对象,进行返回
            return item.getName();
        }, Collectors.toList())));

        //   Map> collect = users.stream().collect(Collectors.groupingBy(Users::getAge, Collectors.mapping(item ->{ return Arrays.asList(item); }, Collectors.toList())));

        collect.forEach((key, value) -> {
            System.out.println("key :   " + key + "    value :   " + value);
        });

你可能感兴趣的:(Java-8-流(1))