spring boot

Java-8-流(1)

2020-01-30  本文已影响0人  Cool_Pomelo

Java-8-流(1)

外部迭代与内部迭代

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

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

  public static void main(String[] args) {

        List<Integer> 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<Integer> integerIterator = data.iterator();

        while (integerIterator.hasNext()){

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

        }

        System.out.println(num);

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

另一种方法就是内部迭代

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


   List<Integer> 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

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

Stream

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

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

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

流的操作类型

在对于一个 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<String> stringStream = Stream.of("sjd","kjfu","4545");

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

        stream1 = Stream.of(strings);

        stream2 = Arrays.stream(strings);

//        //由集合构成,最常用了
        List<String> 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<PersonDto> {


    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<String> 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<String> stringStream_1 = Stream.of("one", "two", "three", "four");

        List<String> list_1 = stringStream_1.collect(Collectors.toList());

        System.out.println(list_1);

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

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


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

        System.out.println(list_2);

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


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


        Set<String> set_1 = stringStream_3.collect(Collectors.toSet());


        System.out.println(set_1);

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

//        Stack<String> 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<String> list = Stream.of("1","2","3")
                .collect(Collectors.toList());

        System.out.println(list);


        Set<Integer> 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<Integer> treeSet = Stream.of(1,2,3,4,5)
                .collect(Collectors.toCollection(TreeSet::new));

        System.out.println(treeSet);


        HashSet<Integer> 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<String,String> 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<Integer> 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<String> strings = Arrays.asList(
                "a",
                "bb",
                "ccc",
                "dddd",
                "eeeee"
        );

        Map<Integer,List<String>> map =

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


        System.out.println(map);



    }
}




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

public class M5 {


    public static void main(String[] args) {

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

        TreeMap<Integer,List<String>> map =

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

        System.out.println(map);


    }
}


分组计数


 public static void main(String[] args) {

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


        Map<Integer,Long> 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<User> users = User_Data.create();


//        统计每个地方的人各有多少
        Map<String,Long> 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<User> users = User_Data.create();

        Map<String,List<User>> 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<Boolean, List<Integer>> map = Stream.of(
                1,2,3,4,5,6
        ).collect(
                Collectors.partitioningBy(x -> x>2)
        );

        System.out.println(map);

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

        Map<Boolean, Long> 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}
        
        

    }

分组和计算每组的总和

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


 public static void main(String[] args) {

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

        Map<Integer,Double> 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);
        
    }

分组和计算每组的总和

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


  public static void main(String[] args) {

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


        Map<Integer,Integer> 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<String> strings = Arrays.asList(
                "a",
                "bb",
                "ccc",
                "dddd",
                "eeeee"
        );

        Map<Integer, Optional<String>> 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<User> 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<String,Long> counting = items.stream().collect(
                Collectors.groupingBy(User::getName,Collectors.counting())
        );


        System.out.println(counting);


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

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

        System.out.println(sum);


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

        System.out.println(groupByPriceMap);


        // group by Salary, uses 'mapping' to convert List<Item> to Set<String>
        Map<BigDecimal, Set<String>> 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 <T>
    Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) 

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

例子:



    public static void main(String[] args) {

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

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

        final Map<Boolean, List<String>> 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<Integer> d = NumData.create();

        Map<Boolean,List<Integer>> 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<String> stream1 = Stream.of(names);
        Stream<String> stream2 = Stream.of(names);
        Stream<String> 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<User> create(){

        List<User> 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<User> users = User_Data.create();

        List<String> 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<Integer, List<String>> collect = users.stream().collect(Collectors.groupingBy(User::getAge, Collectors.mapping(item ->{

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

        //   Map<Integer, List<Object>> 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);
        });

上一篇 下一篇

猜你喜欢

热点阅读