jdk自从8开始,添加了lambda表达式,为了兼容lambda表达式,所以对接口也做了修改,添加了default关键词。这是jdk8与7最大的一个变动。
自从有了lambda表达式之后,项目中被大量运用,但是对于我本人来讲,我其实是挺反对用lambda表达式的,我也一直不用它。原因有三:
其一,就是它虽然让代码变的简洁,但是简洁的结果就是造成语法并不为常人理解,我觉得它的语法上并不是通俗易懂的,需要翻看说明才能看的懂;
其二,没法调试,它采用流的方式进行处理,内存的逻辑根本没法调试;
其三,性能方面,对于数据量少,并发量不大的情况下,我觉得性能很差,这个文章后面也会简单的做下比较。有人说高并发大数据量会有性能优势,但是并发量要大到什么程度,数据量要大到什么程度,没有验证过。而且我们实际工作中,不会一次性处理大量数据,一般都会分页/分批处理。高并发也会进行请求的分发,实际到一台服务器上也不会有很大的并发。
那接下来简单介绍下lambda表达式中使用Collectors.toMap的例子,因为今天review代码的时候刚好注意到它了,就简单研究了下。
这个方法主要是用在我们一个list集合中的元素,要根据一定的条件,将list的集合中的元素内容,放到map集合中。直接上代码吧:
public class TestStream {public static void main(String[] args) {List<Person> list = new ArrayList<>();list.add(new Person("zs","sssss"));list.add(new Person("zs","ddddd"));list.add(new Person("ls","sssss"));list.add(new Person("wk","wwwww"));long time1 = System.currentTimeMillis();//使用lambda表达式进行处理Map<String, String> resultMap = list.stream().collect(Collectors.toMap(Person::getName, item->item.getAddress(),(a, s)->s+","+a));long time2 = System.currentTimeMillis();//使用常规方法处理Map<String, String> result = new HashMap<>();for(Person p : list){result.put(p.getName(),result.get(p.getName()) == null? p.getAddress() : result.get(p.getName())+","+p.getAddress());}long time3 = System.currentTimeMillis();for(Map.Entry<String, String> entry : resultMap.entrySet()){System.out.println(entry.getKey()+"======="+entry.getValue());}System.out.println((time2-time1)+"======="+(time3-time2));for(Map.Entry<String, String> entry : result.entrySet()){System.out.println(entry.getKey()+"======="+entry.getValue());}}
}class Person{private String name;private String address;public Person(String name, String address){this.name = name;this.address = address;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}
}
简单介绍下代码干了啥。代码中有个Person类,有name和address两个属性。 一个list集合中放了多个person对象,并且person对象有name重复,但是address不一样的记录。
现在要把list中的元素放到一个map集合中,key是name值,value是address值。 对于name重复的,让他们的address值合并到一个name中,用逗号隔开,存到map里。
lambda表达式用了一行就解决了。但是常规方法,用了四行代码。
lambda表达式将list先转化为stream()流,调用collect()方法,参数是个Collector。Collectors提供了很多返回Collector的静态方法,toMap就是其中之一。
toMap有三个参数,第一个是key生成的方法/函数,第二个是value生成的方法/函数,第三个是对于重复key的合并逻辑方法/函数。
对于第一和第二个参数,使用Person::getName() / Person::getAddress()方法也行,使用item->item.getName() / item -> item.getAddress()也行,这是两种方式。item是个代名词,你改成任何字母或者单词(关键词除外)标识都行。
对于第三个参数,(a,s)->a+","+s 这个段代码,是个条件函数,是处理对于重复key的情况下的合并处理逻辑。
其中a,s分别代表重复的两个name对应的address值,a/s只是个标识,你用其他任何字母代替都行,但是a和s是有顺序的,在(a,s)的写法中, a表示list集合中靠前的值,s是靠后的值。(如果写成(s,a),那么s就是靠前的值,a就是靠后的值), 所以如果这个函数表达式写成 (a,s)->a ,意思就是如果遇到重复的name(因为map的key是不会重复的),那么map中value的值取排序靠前name对应的address值。 用上面的代码例子来说的话,最后map中key为zs的value值就是sssss。
如果写成(a,s)->s, 也就是如果遇到重复的name,那么map中value的值就是让后面的address覆盖前面的address。用上面的代码例子来说的话,最后map中key为zs的value值就是ddddd。
好了,toMap的介绍就到这里了。下面就看下性能吧。
直接看下我本地运行的例子的打印结果吧:
99是毫秒,也就是lambda表达式这一行,就运行了99ms,但是常规方法,四行运行了0ms。总共才4条数据,耗费了99ms。看到性能了么。99ms,对于一个接口来讲,意味着1秒钟也就支持10个左右的并发。这也是为什么我不太喜欢我们兄弟们使用lambda表达式最主要的原因。
言尽于此,对于jdk自己推出了lambda表达式,那么性能方面他们应该也会考虑去优化,对于朋友们用不用lambda表达式,还是根据自己的实际情况及意愿来吧。