Predicate

  • 主要功能是验证数据

    //源码
    public interface Predicate<T> {
    
        /**
         * 制定检查模板
         */
        boolean test(T t);
    
        /**
        * 得到相反结果
        */
        default Predicate<T> negate() { 
            return (t) -> !test(t); //相当于new了一个Predicate对象并重写了其test方法,如此可以进行链式书写
        }
    
        ……
    }
    
    //调用
    Predicate<String> predicate = (s) -> s.length() > 0; //重写test
    
    predicate.test("foo"); // true
    predicate.negate().test("foo"); // negate返回一个Predicate对象,调用这个对象的test方法;false
    
    Predicate<Boolean> nonNull = Objects::nonNull; //重写test
    Predicate<Boolean> isNull = Objects::isNull;
    
    Predicate<String> isEmpty = String::isEmpty;
    Predicate<String> isNotEmpty = isEmpty.negate();
    

    Consumer

  • 主要功能是更改输入参数的内部状态

    //源码
    public interface Consumer<T> {
    
        void accept(T t);
    
        /**
         * 检测入参是否为空并返回一个实现了方法的Consumer对象
         * 用于链式书写:consumer.andThen().accept()
         */
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); }; //既对调用andThen的对象应用accept方法,也对传入andThen的参数应用accept方法
        }
    }
    
    //调用
    User user=new User("zm");
    //接收一个参数
    Consumer<User> userConsumer=User1 -> User1.setName("zmChange");
    userConsumer.accpet(user);
    System.out.println(user.getName());
    

    Predicate和Consumer综合应用

  • 通过一个学生例子:Student类包含姓名、分数和费用,每个学生科根据分数获得不同程度的费用折扣
  • Student类:

    public class Student {
    
        String firstName;
    
        String lastName;
    
        Double grade;
    
        Double feeDiscount = 0.0;
    
        Double baseFee = 2000.0;
    
        public Student(String firstName, String lastName, Double grade) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.grade = grade;
        }
    
        public void printFee(){
            Double newFee = baseFee - ((baseFee * feeDiscount)/100);
            System.out.println("The fee after discount: " + newFee);
        }
    }
    
  • 用一个Predicate接口判断输入的Student对象是否拥有费用打折的资格,然后使用Consumer接口更新Student对象的折扣:

    public class PredicateConsumerDemo{
        public static Student updateStudentFee(Student student, Predicate<Student> predicate,
                                               Consumer<Student> consumer){
            if(predicate.test(student)){
                consumer.accept(student);
            }
            return student;
        }
    }
    
  • 调用updateStudentFee()

    Stream stream=Stream.of("a","b","c"); //构造stream
    String[] strArray=new String[]{"a","b","c"};
    stream=Arrays.stream(strArray); //底层也是构造stream
    List<String> list=Arrays.asList(strArray); //底层是new ArrayList<>(strArray);
    stream=list.stream(); //底层也是stream
    IntStream.of(new int[]{1,2,3}).forEach(System.out::println);
    
    Student student=new Student("Ashok","Kumar",9.5);
    
    student=PredicateConsumerDemo.updateStudentFee(student,
                                                    stu -> stu.grade > 8.5,
                                                    stu -> stu.feeDiscount = 30.0);
    student.printFee(); //The fee after discount:1400.0
    

    Supplier

  • 主要是返回一个实例,相当于工厂

    //实例化一个String对象,值为hello world
    Supplier<String> supplier=() -> "hello world"; 
    System.out.println(supplier.get());
    
    //实例化一个对象
    Supplier<Student> student=() -> new Student();
    student.get(); //获取此对象
    

    Function

  • 主要功能是对输入的参数进行计算并返回结果
  • 源码:

    public interface Function<T, R> { //指定入参、返回值的类型
    
        /**
         * 对输入的参数进行某种计算并返回定义接口时指定的类型
         */
        R apply(T t);
    
        /**
         * 返回一个Function对象,重写其apply方法为:先执行before的apply方法再执行调用者的apply方法
         */
        default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
            Objects.requireNonNull(before);
            return (V v) -> apply(before.apply(v));
        }
    
        /**
         * 先执行调用者的apply方法再执行after的apply方法
         */
        default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t) -> after.apply(apply(t));
        }
    
        /**
         * static:静态方法;<T>:泛型方法;
         * Function<T,T>:返回值为一个Function对象,
         *                此对象被重写的apply方法入参和返回值类型均为T
         * 功能:返回一个Function对象,其apply方法为传入什么返回什么
         */
        static <T> Function<T, T> identity() {
            return t -> t;
        }
    }
    
  • 调用

    Function<Integer,Integer> name=e -> e*2;
    Function<Integer,Integer> square=e -> e*e;
    
    int value=name.andThen(square).apply(3);
    System.out.println(value); //36
    
    value=name.compose(square).apply(3); //18
    System.out.println(value);
    
    String identity=(String)Function.identity().apply("hello");
    System.out.println(identity); //hello
    

    BiFunction

  • BiFunction与Function的区别就是传入两个参数,依旧返回一个参数(比如传入两个字符串连接成一个返回)
  • 调用

    Function<String,Integer> myFunction=str -> str.length();
    BiFunction<String,String,String> myBiFunction=(str1,str2) -> str1+str2;
    
    int len=myFunction.apply("hello");
    System.out.println(len); //5
    
    String str=myBiFunction.apply("hello","world");
    System.out.println(str); //hello world
    

    Comparator

  • 主要功能是比较任意类型数据,多用于排序、分组
  • 源码:(不是很懂为什么可以有两个抽象方法,但是要实现Comparator接必须实现compare(),equals()随意

    public interface Comparator<T> {
        /**
         * 比较两个参数,数字一般相减,字符串一般用compareTo()
         * 返回1,0,-1,以该接口为入参之一的Collection.sort()分别进行升序、不处理、降序处理
         */
        int compare(T o1, T o2);
    
        /**
         * 判断调用者是否和入参相等,同Object.equals()
         */
        boolean equals(Object obj);
    
        /**
        * 还有一些默认函数,不常用
        */
    }
    
  • 排序应用

    public class FormulaImpl{
    
        //首先定义一个狗狗的类
        class Dog{
            public int age;
            public String name;
    
            public Dog(int age,String name){
                super();
                this.age=age;
                this.name=name;
            }
    
            @Override
            public String toString() {
                return "Dog{" + "age=" + age + ", name='" + name + '\'' + '}';
            }
        }
    
        //然后用Comparator来排序
        public static void main(String[] args) {
    
            //这里注意静态方法里不能有非静态的成员,一定要先实例外部类再实例内部类
            List<Dog> list=new ArrayList<>();
            list.add(new FormulaImpl().new Dog(1,"a"));
            list.add(new FormulaImpl().new Dog(2,"b"));
            list.add(new FormulaImpl().new Dog(3,"c"));
    
            //按照年龄降序
            Comparator<Dog> comparator=(obj1,obj2) -> obj2.age-obj1.age; //升序:obj1.age-obj2.age
            Collections.sort(list,comparator);
            System.out.println("按照年龄降序:"+list); //[Dog{age=3, name='c'}, Dog{age=2, name='b'}, Dog{age=1, name='a'}]
    
            //按照字母顺序升序
            comparator=(obj1,obj2) -> obj1.name.compareTo(obj2.name); //[Dog{age=1, name='a'}, Dog{age=2, name='b'}, Dog{age=3, name='c'}]
            Collections.sort(list,comparator);
            System.out.println("按照字母顺序升序:"+list);
    
        }
    }
    
  • 分组应用

    public class FormulaImpl{
    
        //首先定义一个苹果类
        class Apple {
            public String color;
            public int weight;
    
            public Apple(String color, int weight) {
                super();
                this.color = color;
                this.weight = weight;
            }
    
            @Override
            public String toString() {
                return "Apple [color=" + color + ", weight=" + weight + "]";
            }
        }
    
        /**
        * 分组:入参为数据源和比较规则,比如吧相同颜色或者同一重量级的分到一组
        * 返回组别的集合
        */
        public static <T> List<List<T>> divider(Collection<T> datas, Comparator<? super T> c) {
            //result是所有组的集合,每个组又单独是一个集合
            List<List<T>> result = new ArrayList<List<T>>(); //result.size()=0
    
            //开始分组:遍历每个苹果
            for (T t : datas) {
                //设置flag
                boolean isSameGroup = false;
                //同已有组的任一成员比较,相等即进组;第一个苹果肯定是不用进此循环直接到下一步的
                for (int j = 0; j < result.size(); j++) {
                    if (c.compare(t, result.get(j).get(0)) == 0) {
                        isSameGroup = true;
                        result.get(j).add(t);
                        break;
                    }
                }
                //不是前面任何组的,单独开组
                if (!isSameGroup) {
                    List<T> innerList = new ArrayList<T>();
                    //以下两行看起来好像是开了新组但是却没把苹果放进去,其实到了遍历集合的时候自然就进去了
                    result.add(innerList);
                    innerList.add(t);
                }
            }
            return result;
        }
    
        //调用
        public static void main(String[] args) {
    
            List<Apple> list=new ArrayList<>();
            list.add(new FormulaImpl().new Apple("红",200));
            list.add(new FormulaImpl().new Apple("红",100));
            list.add(new FormulaImpl().new Apple("绿",201));
            list.add(new FormulaImpl().new Apple("绿",101));
            list.add(new FormulaImpl().new Apple("黄",102));
            list.add(new FormulaImpl().new Apple("黄",202));
    
            Comparator<Apple> comparator=(obj1,obj2) -> obj1.color.compareTo(obj2.color);
            List<List<Apple>> byColor=divider(list,comparator);
            System.out.println("按照颜色分组:"+byColor);
    
            comparator=(obj1,obj2) -> (obj1.weight/100 == obj2.weight/100) ? 0 : -1;
            List<List<Apple>> byWeight=divider(list,comparator);
            System.out.println("按照重量分组:"+byWeight);
        }
    }
    
  • 从这个例子中我们可以看出,当一个数组需要按照不同的规则排序的时候,我们可以定义不同的比较器传入排序方法中。

    Stream

  • 主要功能是对集合转换成的数据流进行过滤、转换、计算等操作
  • 除了Stream,平行的还有IntStream、LongStream、DoubleStream三种数值型Stream,当然我们也可以用Stream来代替,但是boxing和unboxing会很耗时。
  • Map接口不支持Stream
  • 源码:

    public interface Stream<T> extends BaseStream<T, Stream<T>> {
    
        /**
         * 返回符合判断条件的流对象
         */
        Stream<T> filter(Predicate<? super T> predicate);
    
        /**
         * 返回经过计算的流对象
         */
        <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    
        /**
         * 返回经过计算的流对象(该计算的结果为int型)
         */
        IntStream mapToInt(ToIntFunction<? super T> mapper);
    
        /**
         * 返回经过计算的流对象,这个流对象的每一个元素都是流
         */
        <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    
        /**
         * 返回经过计算的int型流对象,这个流对象的每一个元素都是int型的流
         */
        IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
    
        /**
         * 去重
         */
        Stream<T> distinct();
    
        /**
         * 元素有得比较则按默认规则排序,否则抛异常
         */
        Stream<T> sorted();
    
        /**
         * 按指定规则排序
         */
        Stream<T> sorted(Comparator<? super T> comparator);
    
        /**
         * 对流的所有元素进行同一个操作
         */
        void forEach(Consumer<? super T> action);
    
        /**
         * 遍历流的元素,与forEach的区别是它返回的是Stream对象,可以继续进行链式操作
         */
        Stream<T> peek(Consumer<? super T> action);
    
        /**
         * 提供一个起始值identity,然后依照运算规则BinaryOperator和所有的元素组合
         * 字符串的拼接、数值的sum、min、max、average都是特殊的reduce
         * 例如:Stream的sum就相当于
         * Integer sum=integers.reduce(0,(a,b) -> a+b);
         * 或Integer sum=integers.reduce(0,Integer::sum);
         */
        T reduce(T identity, BinaryOperator<T> accumulator);
    
        /**
         * 还有很多方法
         */
    }
    
  • 流的构造:

    //用数组构造
    Stream stream=Stream.of("a","b","c"); //构造stream
    String[] strArray=new String[]{"a","b","c"};
    stream=Arrays.stream(strArray); //底层也是构造stream
    
    //用集合构造
    List<String> list=Arrays.asList(strArray); //底层是new ArrayList<>(strArray);
    stream=list.stream(); //底层也是构造stream
    
  • 数值流的构造

    IntStream.of(new int[]{1,2,3});
    
  • 流转换为其他数据结构

    //转换成数组
    Stream stream=Stream.of("a","b","c");
    String[] strArray= (String[]) stream.toArray(String[]::new); //a,b,c
    //转换成集合
    stream=Stream.of("a","b","c"); //Stream只能使用一次,用完即关闭,需重开
    List<String> list1= (List<String>) stream.collect(Collectors.toList()); //a,b,c
    stream=Stream.of("a","b","c");
    List<String> list2= (List<String>) stream.collect(Collectors.toCollection(ArrayList::new)); //a,b,c
    //转换成String
    stream=Stream.of("a","b","c");
    String str=stream.collect(Collectors.joining()).toString(); //abc
    
  • 调用map()

    //转换大写
    List<String> list=new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    List<String> output=list.stream()
            .map(String::toUpperCase)
            .collect(Collectors.toList());
    System.out.println(output);
    
    //平方数
    List<Integer> nums=Arrays.asList(1,2,3,4);
    List<Integer> squareNums=nums.stream()
            .map(n -> n*n)
            .collect(Collectors.toList());
    System.out.println(squareNums);
    
  • flapMap()

    //flatMap把Stream中的层级结构扁平化,直接将最底层元素抽取出来构成Stream
    Stream<List<Integer>> inputStream=Stream.of(Arrays.asList(1),
                                                Arrays.asList(2,3),
                                                Arrays.asList(4,5,6));
    Stream<Integer> outputStream=inputStream.flatMap(midList -> midList.stream());
    List<Integer> list2=outputStream.collect(Collectors.toList()); //Stream不能直接打印,打印出来是一串看不懂的类标志
    System.out.println(list2); //[1, 2, 3, 4, 5, 6]
    
  • peek()

    List<String>list=Stream.of("one","two","three","four").filter(e -> e.length()>3).peek(e -> System.out.println("Filter value:"+e))
            .map(String::toUpperCase).peek(e -> System.out.println("Mapped value:"+e))
            .collect(Collectors.toList()); //一定要转换成List才会打印内容出来
    
  • reduce():有起始值的话一定返回确定的类型,否则可能由于元素不够而判断不出类型返回Optional

    //字符串连接
    String concat=Stream.of("A","B","C","D").reduce("",String::concat); //ABCD
    //求最小值
    double min=Stream.of(-1.5,1.0,-3.0,-2.0).reduce(Double.MAX_VALUE,Double::min); //-3.0
    //求和
    int sum=Stream.of(1,2,3,4).reduce(0,Integer::sum); //10
    //过滤+字符串连接
    concat=Stream.of("a","B","c","D","e","f").filter(x -> x.compareTo("Z")>0)
                                             .reduce("",String::concat);
    
  • limit()&skip()

    //Person类
    class Person{
        public int no; //序号
        private String name;
    
        public Person(int no, String name) {
            this.no = no;
            this.name = name;
        }
    
        //注意:getName方法有打印操作
        public String getName(){
            System.out.println(name);
            return name;
        }
    }
    
    //塞数据
    List<Person> persons=new ArrayList<>();
    for (int i=0;i<11;i++){ //塞11个
        Person person=new FormulaImpl().new Person(i,"name"+i);
        persons.add(person);
    }
    //调用方法
    List<String> personList=persons.stream()
            .map(Person::getName)
            .limit(10) //map管道被限制到只打印前10个
            .skip(3) //personList先被限制到前十又被跳过前三
            .collect(Collectors.toList()); //第一次打印:输出0~10
    //第二次打印
    System.out.println(personList); //输出3~10
    
    • limit()&skip()对sorted()无作用

      List<Person> persons=new ArrayList<>();
      for (int i=0;i<5;i++){
          Person person=new FormulaImpl().new Person(i,"name"+i);
          persons.add(person);
      }
      List<Person> personList=persons.stream()
              .sorted((p1,p2) -> p1.getName().compareTo(p2.getName())) //不受限制,打印5个
              .limit(2)
              .collect(Collectors.toList());
      System.out.println(personList); //输出2个
      
      • 打印结果:

        name1
        name0
        name2
        name1
        name3
        name2
        name4
        name3
        [com.java1234.tool.FormulaImpl$Person@18769467, com.java1234.tool.FormulaImpl$Person@46ee7fe8]
        
      • 上面的打印结果说明sorted的底层是两个两个元素拿出来比较的
  • max()&min()

    //找出最长一行的长度
    BufferedReader br = new BufferedReader(new FileReader("c:\\SUService.log"));
    int longest = br.lines(). //br.lines()得到的是一个Stream对象
     mapToInt(String::length).
     max().
     getAsInt();
    br.close();
    
  • distinct()

    //找出全文的单词,转小写,去重,并排序
    BufferedReader br=new BufferedReader(new FileReader("d:\\test.txt"));
    List<String> words=br.lines().flatMap(line -> Stream.of(line.split(" ")))
                                .filter(word -> word.length()>0)
                                .map(String::toLowerCase)
                                .distinct()
                                .sorted()
                                .collect(Collectors.toList());
    br.close();
    
  • allMatch()&anyMatch()&noneMatch():Stream中全部、任一、无一元素符合传入的Predicate,返回true

    • 在Person类中加个年龄属性和获取年龄的方法,做以下测试

      List<Person> persons = new ArrayList();
      persons.add(new Person(1, "name" + 1, 10));
      persons.add(new Person(2, "name" + 2, 21));
      persons.add(new Person(3, "name" + 3, 34));
      persons.add(new Person(4, "name" + 4, 6));
      persons.add(new Person(5, "name" + 5, 55));
      boolean isAllAdult = persons.stream().
       allMatch(p -> p.getAge() > 18); //false
      boolean isThereAnyChild = persons.stream().
       anyMatch(p -> p.getAge() < 12); //true
      
  • count():计数,返回Stream中元素的个数,返回值类型是long;是一个最终操作

    long startsWithB = stringCollection.stream()
                            .filter((s) -> s.startsWith("b"))
                            .count();
    

    并行Stream

  • Stream有串行和并行两种,串行是在一个线程中依次完成,而并行则是在多个线程上同时执行,可提高效率。
  • 下面我们来创建一个无重复的大容量List

    int max=1000000;
    List<String> list=new ArrayList<>(max);
    for (int i=0;i<max;i++){
        UUID uuid=UUID.randomUUID();
        list.add(uuid.toString());
    }
    
  • 串行排序

    long t0=System.nanoTime();
    list.stream().sorted();
    long t1=System.nanoTime();
    long millis=TimeUnit.NANOSECONDS.toMillis(t1-t0);
    System.out.println(String.format("Sequential sort took: %d ms",millis)); //899mx
    
  • 并行排序,效率提高一倍

    t0=System.nanoTime();
    list.parallelStream().sorted();
    t1=System.nanoTime();
    millis=TimeUnit.NANOSECONDS.toMillis(t1-t0);
    System.out.println(String.format("Parallel sort took: %d ms",millis)); //472ms
    

    参考文章