Java8:lambda表达式

基本定义:

简单理解:lambda表达式相当于创建的一个匿名对象.
每个lambda表达式都与一个特定的Java接口相关联,同时这个函数式接口只能包含一个抽象方法声明。可以在接口上添加@FunctionalInterface注解注明此接口为lambda表达式相关接口。
例如:
声明一个函数式接口Converter:

@FunctionalInterface
public interface Converter<F, T> {

    T convertor(F from);

//    T convertor2(F from);

}

通过lambda表达式生成一个Converter类型的对象:

        Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
        Integer converted = converter.convertor("8");
        System.out.println(converted);

Java8允许通过 :: 关键字传递方法或者构造函数的引用。

传递静态方法

例如:

        Converter<String, Integer> staticFunConverter = Integer::valueOf;
        Integer staticFunConverted = staticFunConverter.convertor("123");
        System.out.println(converted);   // 123

传递对象方法

例如:
新建一个类Something

 static class Something {
        String startsWith(String s) {
            return String.valueOf(s.charAt(0));
        }
    }

通过:: 关键字引用Something类型对象的方法:

    Something something = new Something();
        Converter<String, String> objFunConverter = something::startsWith;
        String objFunConverted = objFunConverter.convertor("Java");
        System.out.println(objFunConverted);//J

传递构造方法

先定义一个多构造函数的bean

    static class Person {
        String firstName;
        String lastName;

        Person() {
        }

        Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }

然后定义一个可以创建person 的personFacotory接口

public interface PersonFactory<P extends FuncTest.Person> {

    P create(String firstName, String lastName);

}

通过构造函数引用,实现工厂接口。

PersonFactory<Person> personFactory = Person::new;
        Person person = personFactory.create("pee", "hao");
        System.out.println(person.lastName);//hao

上面的例子中通过Person::new创建一个Person类构造函数的引用,Java编译器会根据PersonFactory.create方法的签名自动选择合适的构造函数。

lambda表达式访问变量范围

lambda表达式访问变量范围和匿名内部类相同。

可访问本地final局部变量。

final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

可访问成员变量和静态变量。

lambda表达式不能访问默认接口方法。

带有扩展方法的接口

public interface Formula {

    double calculate(int a);

    //JAVA8中可以通过default关键字在接口中添加一个非抽象方法(即:扩展方法)
    default double sqrt(int a) {
        return Math.sqrt(a);
    }

}
Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a);
    }
};
System.out.println(formula.calculate(9));
System.out.println(formula.sqrt(9));

Formula formula1 = Math::sqrt;//此处无法通过直接调用扩展方法sqrt实现。

Java8内置的函数式接口

已经存在的Comparator接口和Runnable接口。

Predicates(断言)

Predicates是包含一个参数返回值为boolean型的函数,为了满足复杂的逻辑条件(or and negate)Predicates接口包含各种默认方法。

 //Predicates(断言)
        Predicate<String> predicate = (s -> s.length() > 0);
        System.out.println(predicate.test("foo"));//true
        System.out.println(predicate.negate().test("foo"));//false

Functions

Functions接口接收一个参数并生成一个结果,可以使用Functions接口的默认方法(compose, andThen)将多个functions串联起来。
注:compose, andThen的区别,compose方法自身先执行,再把执行结果传递给调用者执行。andThen先执行前一步的调用者,获取前一步执行结果后再执行。

//Functions
        Function<Integer, Integer> time2 = e -> e * 2;
        Function<Integer, Integer> squared = e -> e * e;

        System.out.println(time2.compose(squared).apply(4));//32
        System.out.println(time2.andThen(squared).apply(4));//64

Consumers

Consumers接口表示在单个输入参数上要执行的操作。

//Consumers
        Consumer<FuncTest.Person> greeter = person -> System.out.println("hello :" + person.firstName);
        greeter.accept(new FuncTest.Person("jiahong", "hao"));//hello:jiahong

Comparators

Optionals

Optionals不是一个函数式接口,而是一个用来防止出现NullPointerException异常的很好工具。

Streams

java.util.Stream表示可以执行一个或多个操作的序列元素,stream的操作可以分为两种类型:中间操作或最终操作。中间操作的返回值还是一个stream,最终操作的返回值是一个确定的类型。可以通过链式调用可以将多个中间操作串联起来。stream上的操作既可以串行执行也可以是并行执行。

List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
Filter

Filter通过断言来对stream中所有的元素进行判断,由于filter是一个中间操作,因此我们可以在filter后面再调用forEach操作,ForEach操作接收一个consumer接口作为参数,这个consumer会在所有通过filter的元素上执行。ForEach是一个最终操作,返回值类型是void,因此我们不能在ForEach后面再调用其他操作。(最终操作是回去执行的操作。这里的forEach相当于for(...)循环了)

        stringCollection
                .stream()   
                .filter(s -> s.startsWith("a"))
                .forEach(System.out::println);
        //aaa2 aaa1
Sorted

Sorted是一个中间操作,sorted操作接收Comparator接口作为参数,返回一个有序的stream。如果传递一个空函数给sorted,会按照自然顺序进行排序,如果元素不支持排序,会在最终操作时抛出异常。

stringCollection
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// aaa1 aaa2
Map

map是一个中间操作,通过给定的转换方法map能将stream中的每个元素映射为另外一种对象,下面的例子中将每个string转换为upper-cased string。map也支持不同类型的映射,具体映射的目标类型取决于映射方法的返回值类型。

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
Match

matching是一个最终操作,返回值类型为boolean型,可以用来确定某种断言是否与stream匹配。

boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));

System.out.println(allStartsWithA);      // false

boolean noneStartsWithZ =
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));

System.out.println(noneStartsWithZ);      // true
Count

Count是一个最终操作,返回stream中元素的个数,返回值类型为long。

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

System.out.println(startsWithB);    // 3
Reduce

Reduce是一个最终操作,通过给定的方法在stream上执行reduce操作,返回值为使用Optional接口包装的reduce值。(Reduce含义:把...归纳为,减少)

Optional<String> reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

Parallel Streams

Parallel Streams能在多个线程上并发地执行,下面的例子中通过简单的使用parallel streams就能大幅度地提高系统性能。
首先我们创建一个很大的元素唯一的list.

int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
    UUID uuid = UUID.randomUUID();
    values.add(uuid.toString());
}

我们开始测试在这个stream上执行排序所需的时间
Sequential Sort

long t0 = System.nanoTime();

long count = values.stream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms", millis));

// sequential sort took: 899 ms

Parallel Sort

long t0 = System.nanoTime();

long count = values.parallelStream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("parallel sort took: %d ms", millis));

// parallel sort took: 472 ms

上面的两段代码几乎相同,但是parallel sort的速度几乎比sequential Sort速度快50%。

Map

Map<Integer, String> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
                //当键值在map中不存在时,才会执行put操作.
            map.putIfAbsent(i, "val" + i);
        }

        map.forEach((key, value) -> System.out.println(value));

        map.computeIfPresent(3, (num, val) -> val + num + 1);
        System.out.println(map.get(3)); // val331 函数式接口返回值与原来的value不同,则赋予新值.

        map.computeIfPresent(9, (num, val) -> null);
        System.out.println(map.containsKey(9));//false 函数式接口返回值为null,则相应的mapping会被删除


        //当键值在map中不存在时,才会计算函数式接口的值,并设值.
        map.computeIfAbsent(23, num -> "val" + num);
        System.out.println(map.containsKey(23));
        System.out.println(map.get(23));

        map.computeIfAbsent(3, num -> null);
        System.out.println(map.get(3));//val331

        map.computeIfAbsent(3, num -> "bam");
        System.out.println(map.get(3));//val331

下面的例子展示了map如何删除value不为空的key的操作。

map.remove(3, "val3");
map.get(3);             // val33

map.remove(3, "val33");
map.get(3);             // null
Another helpful method:

System.out.println(map.getOrDefault(42, "not found"));  // not found
System.out.println(map.get(42));//null

//Merging entries of a map is quite easy:
map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9concat

merge方法在key不存在时,可以向map中添加key/value,当key存在时,可以对这个key对应的value执行merge操作。

todo:stream()方法中的collect()需要总结下,涉及到了经常要用到的list装换map

JAVA8 教程
Java8 lambda表达式10个示例
【译】Java 8的新特性—终极版(杜琪)
Java8初体验(二)Stream语法详解
Java8 新特性之流式数据处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容