玩一下Lambda和StreamAPI

什么东西会了的话就很简单,对于不会的人永远就是非常难。
之前我一直说Lambda太反人类,这玩意儿真难,淦。

之前跟海螺dalao和仓鼠dalao一直吐槽Lambda忒难了,这啥东西都是,他们两个人没有例外都用同一句话怼回我
——这有啥难的。

今天下JDK的时候猛然发现,其实Java已经出Java14了。Java8都出了老久了,但是自己还没摸清楚这次更新最重要的特性,实在是惭愧不已。

于是今天来摸一摸Lambda,StreamAPI.
在各种百度、Google、翻资料、看视频和看书之后,大致上明白了是什么意思了。
虽然根本就没弄清楚没吃透,但是现在我已经可以觉得。。。。。
——诶,Lambda牛逼
——真香!

函数式接口

Java8给出了新东西,函数式接口。具体内容我其实没整明白,但是我感觉其中最靓的地方有两点:

  1. 抽象方法只能有一个。
  2. default方法体。

第一点没啥可说的,函数式接口特性。第二点就爽到不行。

之前跟别人一起写东西,假如你要是随随便便把接口给改了,比如你想给接口加个新方法了,那基本上就是约等于找骂,因为别人用了你的接口整出来的类都得适配你这个修改,你一改大家都要改。
这个就很可怕了,久而久之你写的东西就没人用了,怕你瞎改接口。
然后你就会被别人孤立,别人会冷落你。
然后你会陷入人生低谷,久而久之心里会出问题,进入心理亚健康的状态,甚至还有危及生命的风险。
我觉得这也是Java程序员风险高的原因(误

而有了这个default方法体,interface定义的方法直接可以整出默认的实现方式,你加了这个方法之后,队友爱改不改,真是关爱生命、挽救生命的壮举,我必须点赞?

Lambda

Lambda基于函数式编程的思路展开,以前要是有个接口,如果你想实现它,然后创建一个实例,这咋弄呢?

public class TestJava {
    public interface DemoInterface {
        public void test();
    }

    public static void main(String[] args) {
        DemoInterface demoInterface = new DemoInterface(){
            @Override
            public void test() {
                System.out.println("test!");
            }
        };

        demoInterface.test();
    }
}

这不难理解,定义好的接口直接实现一下,调用实例的对象就成了。
但是拼一拼,这个DemoInterface是一个函数式接口,它肯定只有一个方法需要复写(也就是test方法)。我写这么多代码去实现这个方法,这多累啊,毕竟我还是个孩子。

然后Lambda就能解决这个问题了。写个啥玩意儿啊,DemoInterface是可以推断出来的,方法名因为只有那一个,所以完全能省去,其实大部分代码都能省啊。
那就赶紧把Lambda安排上,看看效果:

public class TestJava {
    public static void main(String[] args) {
        DemoInterface demoInterface = () -> {
            System.out.println("test!");
        };
        demoInterface.test();
    }

    public interface DemoInterface {
        public void test();
    }
}

成了,现在我把匿名类的定义和方法的定义大部分都给省了,如果test方法有参数比如String str,其实在main那里用的时候,这个参数类型也能省去,画风大概是这样的:

public class TestJava {
    public static void main(String[] args) {
        DemoInterface demoInterface = (str) -> {
            System.out.println(str);
        };
        demoInterface.test("test!!!");
    }

    public interface DemoInterface {
        public void test(String str);
    }
}

其实还能继续省,因为体里只有一行sout,这种一行代码的话直接就能把花括号给它去了。
main方法里头就变成这样了:

DemoInterface demoInterface = (str) -> System.out.println(str);
demoInterface.test("test!!!");

最后我们实现这个方法并调用加起来,实现一共一行,调用一共一行,代码变少了,可阅读性更高了,这就很帅。

StreamAPI

Java8还有一个厉害的地方就是StreamAPI.

举个例子,比如以前要给List<Integer> nums = Arrays.asList(1,4,2,6,7,5);每个数字都加个1,这咋整?
其实一个for循环就能解决问题了。

List<Integer> nums = Arrays.asList(1,4,2,6,7,5);
List<Integer> newNums = new ArrayList<>();
for(int n:nums)
    newNums.add(n+1);
nums = newNums;

但是用上StreamAPI之后,这就忒简单了,画风是这个样的:

List<Integer> nums = Arrays.asList(1,4,2,6,7,5);
nums = nums.stream()
        .map(n->n=n+1) //这里别写n++!!!
        .collect(Collectors.toList());

瞬间就感觉写代码跟起飞的似的。

现在就可以把List啊数组啊这种转换为Stream,进行操作,最后用collect可以把Stream重新收集为指定的类型。

当然,骚操作不局限于此,还有很多操作,都很骚。

并行(xíng)

上面那种处理是串行Stream,就是挨着顺序处理。
也有并行处理,并行处理处理数组类的流比较快。

有状态和无状态

要是非得说这俩词是什么意思,那么肯定是解释不清。但是意思理解了就通了。

我自己觉得主要区别体现在几个应用实例上,比如操作sort是有状态操作,对数字排序,要是并行时,因为它弄的是类似“一组一组来,最后和一起”的走法,那这样弄毛病就很大,数据量稍微大一点,排序结果就是乱的。
所以排序是一种有状态的操作,这种操作的执行要看别人,但是有些“只靠自己”的操作,比如map,这种就是逐一修改型的操作,是中无状态操作。

这样理解肯定有问题,但是我觉得没啥大问题。

咋调试

其实我看到的最大的问题就是这样调试不方便。
假如是for写出来,用IDEA的debugger可以断点看到各个步骤,但是这样用StreamAPI一弄,这咋看哪里出问题了?
或者说,我看不清楚各个步骤的问题。

后来终于发现,IDEA有Stream Trace,Stream操作可以一步步看操作过程。

DEBUGGER用的时候在stream第一行断个点就行了,忒爽了。数字咋变的,数据位置咋变的,都标的清清楚楚的。

爱了爱了!

总结

当然,用法根本不局限于此,具体我还要多学习学习。
总之,Java8的这些特性是真的香。

上一篇
下一篇