函数式编程

函数式编程一般结合集合使用,Guava对集合的操作进行了抽象,共有两种:

  • 变换(transform)
  • 过滤(filter)

如何实现函数式

Java是静态语言,JDK8之前不支持lambda。如何实现函数式将一个函数(闭包)作为参数传入另一个方法中?只能只用一个接口封装一下函数了,需要使用的时候,new一个接口的匿名实现类,这样模拟一个闭包。

对于变换过滤操作,Guava实现了两个接口:

  • Function
  • Predicate

分别用于变换过滤操作。

问题

有这样的一个List,"ZHANG", "Chen", "LI", "wang", "ZHAO",求字符串全部为大写的长度,返回一个集合。

传统写法:

@Test
public void test5() {
    List<String> list = Lists.newArrayList("ZHANG", "Chen", "LI", "wang", "ZHAO");
    List<Integer> length = Lists.newArrayList();
    for (String tmp : list) {
        if (CharMatcher.JAVA_UPPER_CASE.matchesAllOf(tmp)) {
            length.add(tmp.length());
        }
    }
    System.out.println(length.toString());
}

Guava非fluent方式:

@Test
public void test3() {
    List<String> list = Lists.newArrayList("ZHANG", "Chen", "LI", "wang", "ZHAO");
    ArrayList<Integer> integers = Lists.newArrayList(Iterables.transform(Iterables.filter(list, new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            //CharMathcher本身也是一个Predicate
            return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(input);
        }
    }), new Function<String, Integer>() {
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    }));
    System.out.println(integers.toString());
}

Guava fluent方式:

@Test
public void test4() {
    List<String> list = Lists.newArrayList("ZHANG", "Chen", "LI", "wang", "ZHAO");
    ArrayList<Integer> integers = Lists.newArrayList(FluentIterable.from(list).filter(new Predicate<String>() {
        @Override
        public boolean apply(String input) {
            return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(input);
        }
    }).transform(new Function<String, Integer>() {
        @Override
        public Integer apply(String input) {
            return input.length();
        }
    }));
    System.out.println(integers.toString());
}

可以看到fluent方式filter和transform之间是没有嵌套的。看上去Guava的函数式方式代码比传统的方式更长,这纯粹是因为Java不支持lambda,每次都需要new的Function和Predicate匿名内部类导致的。

看下如果支持lambda的Java8上的代码:

@Test
public void test4_() {
    List<String> list = Lists.newArrayList("ZHANG", "Chen", "LI", "wang", "ZHAO");
    ArrayList<Integer> integers = Lists.newArrayList(FluentIterable.from(list)
            .filter(input -> CharMatcher.JAVA_UPPER_CASE.matchesAllOf(input))
            .transform(input->input.length()));
    System.out.println(integers.toString());
}

使用了lambda,代码就简洁了很多。变换和过滤可以看成两种操作符,这和RxJava很像,不过RxJava做的更通用,表现在:

  • Guava的操作符通常作用于集合数据,RxJava通过Observable可以从任何地方获取数据。
  • RxJava操作符更多,更灵活。

RxJava版的代码

@Test
public void test2_() {
    List<String> list = Lists.newArrayList("ZHANG", "Chen", "LI", "wang", "ZHAO");
    List<Integer> lengths = Observable.from(list).filter(new Func1<String, Boolean>() {
        @Override
        public Boolean call(String s) {
            return CharMatcher.JAVA_UPPER_CASE.matchesAllOf(s);
        }
    }).map(new Func1<String, Integer>() {
        @Override
        public Integer call(String s) {
            return s.length();
        }
    }).toList().toBlocking().single();
    System.out.println(lengths);
}

RxJava所有封装函数的接口都叫FuncX,根据参数的不同有Func1、Func2...Func9。

注意点

Guava的函数式做的并不“智能”,对于上述问题,传统写法只要扫描集合一次,但Guava先filter后transform的方式需要扫描集合两次。 据说RxJava不存在这个问题,原理没有细研究过。可以猜猜,应该是先扫描了有哪些操作符,然后进行了优化。

results matching ""

    No results matching ""