全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Java中Lambda表达式并行与组合行为

从串行到并行

串行指一个步骤一个步骤地处理,也就是通常情况下,代码一行一行地执行。

如果将我们常用的迭代器式的循环展开的话,就是串行执行了循环体内所定义的操作:

sum += arr.get(0);
sum += arr.get(1);
sum += arr.get(2);
//...

在书的一开始,就提到Java需要支持集合的并行计算(而Lambda为这个需求提供了可能)。

这些功能将全部被实现于库代码中,对于我们使用者,实现并行的复杂性被大大降低(最低程度上只需要调用相关方法)。

另外,关于并发与并行这两个概念,其实是不同的,如果不明白的话请自行了解,在此只引用一句非常流行的话:

一个是关于代码结构,一个是关于代码执行。

如果我们想将一个计算任务均匀地分配给CPU的四个内核,我们会给每个核分配一个用于计算的线程,每个线程上进行整个任务的子任务。

书上有一段非常形象的伪代码:

if the task list contains more than N/4 elements {
 leftTask = task.getLeftHalf()
 rightTask = task.getRightHalf()
 doInparallel {
 leftResult = leftTask.solve()
 rightResult = rightTask.solve()
 }
 result = combine(leftResult, rightResult)
} else {
 result = task.solveSequentially()
}

代码中,将每四个任务元素分为一组,用四个内核对其进行并行处理,然后每两组进行一次结果的合并,最终得到整个任务队列的最终结果。

从整体处理流程上看,先将任务队列递归地进行分组,并行处理每一组,然后将结果递归地进行合并(合并通过管道终止操作实现)。

Java8之前,开发者们使用一种针对集合的fork/join框架来实现该模式。

然而现在,想对代码进行性能优化,就是一件非常容易的事了。

还记得我们上一节中所得出的最终代码:

long validContactCounter = contactList.stream()
 .map(s -> new Contact().setName(s))
 .filter(Contact::call)
 .count();

稍加改动:

long validContactCounter = contactList.parallelStream()
 .map(s -> new Contact().setName(s))
 .filter(Contact::call)
 .count();

注意stream()变为parallelStream()

同时下图将展示如何根据四个核对上述任务进行分解处理,最终合并结果并终止管道。

注意递归分解的目的是使子任务们足够小来串行执行。

组合行为

Java写手应该知道,Java中并不存在纯粹的“函数”,只存在“方法”。也就是说,Java中的函数必须依赖于某一个类,或者作为类的某种行为存在。

而在其他语言中,存在纯函数,以CoffeeScript的语法,声明一个函数:

eat = (x) -> 
 alert("#{x} has been eatten!")

这种写法与Lambda表达式的语法非常相近,也就是说,相比于匿名内部类,Lambda表达式看上去更像是一种函数表达式。

对于函数,一个核心操作便是组合。如果要求一元二次函数的其中一个解sqrt(sqr(b) - 4 * a * c),便是对多个子函数进行了组合。

对于面向对象,我们通过解耦的方式来分解它,同样,我们也希望以此种方式分解一个函数行为。

首先,沿用上两节中使用的例子,对Contact类稍作修改,将name属性分拆为名和姓:

private String firstName;
private String lastName;

假设我们现在想要对联系人们进行排序,创建自定义排序的Java标准方式是创建一个Comparator:

public interface Comparator<T> {
 int compare(T o1, T o2);
 //...
}

我们想通过比较名的首字母来为联系人排序:

Comparator<Contact> byFirstName = new Comparator<Contact>() {
 @Override
 public int compare(Contact o1, Contact o2) {
 return Character.compare(o1.getFirstName().charAt(0), o2.getFirstName().charAt(0));
 }
};

Lambda写法:

Comparator<Contact> byFirstNameLambdaForm = (o1, o2) ->
 Character.compare(o1.getFirstName().charAt(0), o2.getFirstName().charAt(0));

写完这段代码后,IDEA立即提醒我代码可以替换为Comparator.comparingInt(...),不过这是后话,暂且不表。

在上面的代码中,我们发现了组合行为,即Comparator<Contact>的compare(...)方法里面还套用了o.getFirstName()与Character.compare(...)这两个方法(为了简洁,这里暂不考虑charAt(...)),在java.util.function中,我们找到了这种函数的原型:

public interface Function<T, R> {
 R apply(T t);
 //...
}

接收一个T类型的参数,返回一个R类型的结果。

现在我们将“比较名的首字母”这个比较键的提取行为抽成一个函数对象的实例:

Function<Contact, Character> keyExtractor = o -> o.getFirstName().charAt(0);

再将“比较首字母”这个具体的比较行为抽出来:

Comparator<Character> keyComparator = (c1, c2) -> Character.compare(c1, c2);

有了keyExtractor和keyComparator,我们再来重新装配一下Comparator:

Comparator<Contact> byFirstNameAdvanced = (o1, o2) ->
 keyComparator.compare(keyExtractor.apply(o1), keyExtractor.apply(o2));

到了这一步,我们牺牲了简洁性,但获得了相应的灵活性,也就是说,如果我们改变比较键为姓而非名,只需改动keyExtractor为:

Function<Contact, Character> keyExtractor = o -> o.getLastName().charAt(0);

值得庆幸的是,库的设计者考虑到了这一自然比较的需求的普遍性,因此为Comparator接口提供了静态方法comparing(...),只需传入比较键的提取规则,就能针对该键生成相应的Comparator,是不是非常神奇:

Comparator<Contact> compareByFirstName = Comparator.comparing(keyExtractor);

即使我们想改变比较的规则,比如比较联系人姓与名的长度,也只需做些许改动:

Comparator<Contact> compareByNameLength = Comparator.comparing(p -> (p.getFirstName() + p.getLastName()).length());

这是一个重大的改进,它将我们所关注的焦点真正集中在了比较的规则上面,而不是大量地构建所必须的胶水代码。

comparing(...)通过接收一个简单的行为,进而基于这个行为构造出更加复杂的行为。

赞!

然而更赞的是,对于流和管道,我们所需要的改动甚至更少:

contacts.stream()
 .sorted(compareByNameLength)
 .forEach(c -> System.out.println(c.getFirstName() + " " + c.getLastName()));

小结

本章的代码:

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
public class Bar {
 public static void main(String[] args) {
//    long validContactCounter = contactList.parallelStream()
//    .map(s -> new Contact().setFirstName(s))
//    .filter(Contact::call)
//    .count();
  List<Contact> contacts = new ArrayList<Contact>() {{
   add(new Contact().setFirstName("Foo").setLastName("Jack"));
   add(new Contact().setFirstName("Bar").setLastName("Ma"));
   add(new Contact().setFirstName("Olala").setLastName("Awesome"));
  }};
  Comparator<Contact> byFirstName = new Comparator<Contact>() {
   @Override
   public int compare(Contact o1, Contact o2) {
    return Character.compare(o1.getFirstName().charAt(0), o2.getFirstName().charAt(0));
   }
  };
  //--- Using Lambda form ---//
  Comparator<Contact> byFirstNameLambdaForm = (o1, o2) ->
    Character.compare(o1.getFirstName().charAt(0), o2.getFirstName().charAt(0));
  Function<Contact, Character> keyExtractor = o -> o.getFirstName().charAt(0);
  Comparator<Character> keyComparator = (c1, c2) ->
    Character.compare(c1, c2);
  Comparator<Contact> byFirstNameAdvanced = (o1, o2) ->
    keyComparator.compare(keyExtractor.apply(o1), keyExtractor.apply(o2));
  Comparator<Contact> compareByFirstName = Comparator.comparing(keyExtractor);
  Comparator<Contact> compareByNameLength = Comparator.comparing(p -> (p.getFirstName() + p.getLastName()).length());
  contacts.stream()
    .sorted(compareByNameLength)
    .forEach(c -> System.out.println(c.getFirstName() + " " + c.getLastName()));
 }
}

以及运行结果:

Bar Ma
Foo Jack
Olala Awesome

以上所述是小编给大家介绍的Java中Lambda表达式并行与组合行为,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


# java  # lambda表达式  # Java大文本并行计算实现过程解析  # Java8并行流中自定义线程池操作示例  # java利用CountDownLatch实现并行计算  # 浅谈在Java中使用Callable、Future进行并行编程  # 浅谈Java Fork/Join并行框架  # Java通过Fork/Join优化并行计算  # Java 并行数据处理和性能分析  # 递归  # 只需  # 的是  # 在此  # 首字母  # 这两个  # 也就是说  # 一个函数  # 小编  # 这是  # 这一  # 是一种  # 就能  # 一句  # 多个  # 而在  # 不明白  # 这段  # 这是一个  # 对其 


相关文章: 制作表格网站有哪些,线上表格怎么弄?  如何选择高效响应式自助建站源码系统?  seo网站制作优化,网站SEO优化步骤有哪些?  潍坊网站制作公司有哪些,潍坊哪家招聘网站好?  学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?  Java解压缩zip - 解压缩多个文件或文件夹实例  如何快速登录WAP自助建站平台?  建站之星代理如何获取技术支持?  移民网站制作流程,怎么看加拿大移民官网?  如何在阿里云完成域名注册与建站?  制作网站的过程怎么写,用凡科建站如何制作自己的网站?  浅析上传头像示例及其注意事项  浙江网站制作公司有哪些,浙江栢塑信息技术有限公司定制网站做的怎么样?  如何高效配置香港服务器实现快速建站?  如何设置并定期更换建站之星安全管理员密码?  如何在VPS电脑上快速搭建网站?  如何确认建站备案号应放置的具体位置?  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  如何获取PHP WAP自助建站系统源码?  网站制作报价单模板图片,小松挖机官方网站报价?  建站之星会员如何解锁更多建站功能?  单页制作网站有哪些,朋友给我发了一个单页网站,我应该怎么修改才能把他变成自己的呢,请求高手指点迷津?  Swift中swift中的switch 语句  如何在橙子建站中快速调整背景颜色?  如何选择最佳自助建站系统?快速指南解析优劣  如何快速搭建安全的FTP站点?  如何快速辨别茅台真假?关键步骤解析  如何在阿里云香港服务器快速搭建网站?  如何在企业微信快速生成手机电脑官网?  音响网站制作视频教程,隆霸音响官方网站?  成都网站制作价格表,现在成都广电的单独网络宽带有多少的,资费是什么情况呢?  如何用wdcp快速搭建高效网站?  高端云建站费用究竟需要多少预算?  如何高效搭建专业期货交易平台网站?  如何用景安虚拟主机手机版绑定域名建站?  建站主机如何安装配置?新手必看操作指南  网页设计与网站制作内容,怎样注册网站?  如何挑选最适合建站的高性能VPS主机?  如何彻底删除建站之星生成的Banner?  建站主机解析:虚拟主机配置与服务器选择指南  宝塔新建站点报错如何解决?  大连 网站制作,大连天途有线官网?  如何快速生成专业多端适配建站电话?  北京网站制作的公司有哪些,北京白云观官方网站?  网站制作知乎推荐,想做自己的网站用什么工具比较好?  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  建站之星如何一键生成手机站?  XML的“混合内容”是什么 怎么用DTD或XSD定义  如何制作一个表白网站视频,关于勇敢表白的小标题?  如何用y主机助手快速搭建网站? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。