引用

https://www.liaoxuefeng.com/wiki/1252599548343744/1305207799545890
https://www.cnblogs.com/SIHAIloveYAN/p/11288064.html
https://www.liaoxuefeng.com/wiki/1252599548343744/1322402873081889

Stream API

Stream 是一种惰性运算

  1. Stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的,故而可以打印”无限“的数
Stream<BigInteger> naturals = createNaturalStream(); // 不计算,无限数流,先不考虑实现
Stream<BigInteger> s2 = naturals.map(BigInteger::multiply); // 不计算
Stream<BigInteger> s3 = s2.limit(100); // 不计算
s3.forEach(System.out::println); // 计算

可以看到,一个Stream转换为另一个Stream时,实际上只存储了转换规则,并没有任何计算发生。

中间操作与终止操作

  • 中间操作是懒执行,即直到实际需要处理结果(终止操作)时才会执行,常见的有
filter()
map()
flatMap()
distinct()
sorted()
peek()
limit()
skip()
  • 终止操作可以遍历流生成结果或直接消费。终止操作执行后,可以认为管道流被消费了并不能再被使用。
forEach()
forEachOrdered()
toArray()
reduce()
collect()
min()
max()
count()
anyMatch()
allMatch()
noneMatch()
findFirst()
findAny()

lambda表达式和方法引用

lambda

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)
lambda表达式可以直接用在,只定义单方法的接口(FunctionalInterface功能接口)上,方法使用@FunctionalInterface注释,从注释中得知单方法定义。

  • 从概念上讲,功能接口仅具有一种抽象方法。 由于默认方法具有实现,因此它们不是抽象的。 如果接口声明了一个覆盖java.lang.Object的公共方法之一的抽象方法,则该方法也不计入接口的抽象方法计数,因为该接口的任何实现都将具有java.lang.Object或其他地方的实现。

编译器自动识别出传入参数(s1,s2)的类型和返回值类型,免去了复杂的匿名类实现

Arrays.sort(array, (s1, s2) -> {
    return s1.compareTo(s2);
});

原实现
Arrays.sort(array, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
});

方法引用

方法引用只要当某个方法签名和接口恰好一致(方法参数一致,返回类型相同),就可以直接把方法名作lombda表达式传入

String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, Main::cmp);
 
Arrays.sort(array, String::compareTo);
 
List<Person> persons = array.stream().map(Person::new).collect(Collectors.toList());
 
static int cmp(String s1, String s2) {
        return s1.compareTo(s2);
    }

可以传入静态方法,实例方法,构造方法

  1. 静态方法,正常传递
Arrays.sort(array, Main::cmp);
  1. 实例方法,实例类型被看做第一个参数类型
Arrays.sort(array, String::compareTo);

public final class String {
    public int compareTo(String o) {
        ...
    }
}

因为实例方法有一个隐含的this参数,String类的compareTo()方法在实际调用的时候,第一个隐含参数总是传入this
相当于静态方法:
public static int compareTo(this, String o);

  1. 传入构造方法时,实例类型被看做返回类型
  • 构造方法
class Person {
    String name;
    public Person(String name) {
        this.name = name;
    }
}
  • 使用
List<String> names = List.of("Bob", "Alice", "Tim");
List<Person> persons = new ArrayList<>();
for (String name : names) {
    persons.add(new Person(name));
}

lambda 作返回值

如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式

  • 创建
public static Comparator<String> getComparator(){
//方法的返回值类型是一个接口,那么我们可以返回这个接口的匿名内部类
        /*return new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //按照字符串的降序排序
                return o2.length()-o1.length();
            }
        };*/
   return (String o1, String o2)-> o2.length() - o1.length();
}
  • 使用
public static void main(String[] args) {
        //创建一个字符串数组
        String [] array={"aaa","bbbb","cc","ddd"};
        //调用Arrays中的sort方法,对字符串数组进行排序
        Arrays.sort(array,getComparator());
}

通过function包使用lambda

了解了lambda表达式与方法引用后,就可以来了解java.util.function包
接口 描述
Predicate//谓词,断言 接收一个传入参数,返回一个boolean
Function<T,R>//功能 接受一个输入参数,返回一个结果
Supplier//供给 无参数,返回一个结果
Consumer//消费 接受一个输入参数,并且不返回任何结果
BiFunction<T,U,R> 接受两个输入参数的方法,并且返回一个结果
BiConsumer<T,U>
接受两个输入参数的操作,并且不返回任何结果

其实就是普通的接口,通过实现,lambda等方式实现后,根据签名一致性,像普通实例一样调用。

一次谓词使用经历,依赖querydsl

  1. dsl中有一个BooleanExpression,他是一个实现了Predicate的抽象类,表示boolean表达式。
public abstract class BooleanExpression extends LiteralExpression<Boolean> implements Predicate
  1. 我编写了一个单一方法返回booleanExpression的接口类QuerySpec,通过QuerySpec来限定谓词使用范围(只被某个扩展于BaseEntity的实体使用,学习时可以不用)。
public interface QuerySpec<T extends BaseEntity> extends Serializable {
    long serialVersionUID = 1L;

    BooleanExpression toExpression();

    static <T extends BaseEntity> QuerySpec<T> not(QuerySpec<T> spec) {
        return spec != null ? () -> {
            return spec.toExpression().not();
        } : null;
    }

    default QuerySpec<T> and(QuerySpec<T> other) {
        return () -> {
            return this.toExpression().and(other.toExpression());
        };
    }

    default QuerySpec<T> or(QuerySpec<T> other) {
        return () -> {
            return this.toExpression().or(other.toExpression());
        };
    }
}
  1. 一个用来组合条件返回booleanExpression的通用抽象类SpecHelper
public abstract class SpecHelper {
    /**
     * 返回一个 参数在指定某个collection内的 布尔表达式
     */
    static <T> BooleanExpression parameterIn(SimpleExpression<T> parameter, Collection<T> collection) {
        if (collection == null) {
            return alwaysTrue;
        }
        return parameter.in(collection);
    }

    /**
     * 返回一个 参数等于指定right值 布尔表达式
     */
    static <T> BooleanExpression parameterEq(SimpleExpression<T> parameter, T right) {
        if (right == null) {
            return alwaysTrue;
        }
        return parameter.eq(right);
    }
}
  1. 规约TestSpec
public abstract class TestSpec {
  public static QuerySpec<LeakPoint> dataAuth() {
  ...
    }
  public static QuerySpec<Test> testType(TestType testType) {
        return () -> parameterEq(QTest.test.testType, testType);
    }
}
......
  1. 使用
QuerySpec<Test> spec = TestSpec.dataAuth();

if (filter != null) {
            spec = spec.and(TestSpec.range(filter.getRange()))
                    .and(TestSpec.Ids(filter.getIds()))
                    .and(TestSpec.testType(filter.getTestType()))
}

通过使用谓词,将多个条件组合起来,在条件为空时不进行筛选,不为空的时候进行筛选,以实现自定义查询。