JavaSE 8 :Lambda 快速学习(四) 完结

本文介绍了Java 8中Lambda表达式的使用方法及其与Collections框架的结合应用,包括Predicate接口的使用、方法引用、流(Stream)API的操作及并行处理等内容。

Lambda Expressions and Collections

前面的章节已经介绍过Function接口,也已经实现基本的Lambda表达式语法示例.这一章节我们重新回顾Lambda表达式如何通过Collection类来提升.

Lambda Expressions and Collections

到目前所创建的示例中,集合只用到一点.但是一些Lambda表达式新特征改变了集合以往的使用方式.这一章节将介绍这些新特征.

新增类

Drivers,pilotsdraftees查询条件都已经被封装到SearchCriteria 类中.

SearchCriteria.java

package com.example.lambda;  
 import java.util.HashMap;
 import java.util.Map;
 import java.util.function.Predicate;
 
 /**
  *
  * @author MikeW
  */
 public class SearchCriteria {
 
   private final Map<String, Predicate<Person>> searchMap = new HashMap<>();
 
   private SearchCriteria() {
     super();
     initSearchMap();
   }
 
   private void initSearchMap() {
     Predicate<Person> allDrivers = p -> p.getAge() >= 16;
     Predicate<Person> allDraftees = p -> p.getAge() >= 18 && p.getAge() <= 25 && p.getGender() == Gender.MALE;
     Predicate<Person> allPilots = p -> p.getAge() >= 23 && p.getAge() <= 65;
 
     searchMap.put("allDrivers", allDrivers);
     searchMap.put("allDraftees", allDraftees);
     searchMap.put("allPilots", allPilots);
 
   }
 
   public Predicate<Person> getCriteria(String PredicateName) {
     Predicate<Person> target;
 
     target = searchMap.get(PredicateName);
 
     if (target == null) {
 
       System.out.println("Search Criteria not found... ");
       System.exit(1);
     
     }
       
     return target;
 
   }
 
   public static SearchCriteria getInstance() {
     return new SearchCriteria();
   }
 }

Predicate依赖的查询条件存储在这个类中,对于我们的测试方法也是可以获取到的.

循环

第一个新特性就是对于每个集合类都有一个forEach新方法.下面是打印Person列表的一些示例.

Test01ForEach.java

public class Test01ForEach {
   
   public static void main(String[] args) {
     
     List<Person> pl = Person.createShortList();
     
     System.out.println("\n=== Western Phone List ===");
     pl.forEach( p -> p.printWesternName() );
     
     System.out.println("\n=== Eastern Phone List ===");
     pl.forEach(Person::printEasternName);
     
     System.out.println("\n=== Custom Phone List ===");
     pl.forEach(p -> { System.out.println(p.printCustom(r -> "Name: " + r.getGivenName() + " EMail: " + r.getEmail())); });
     
   }
 
 }

第一个例子展示了一个标准的Lambda表达式调用printWesternName方法打印出在列表中的每个人。第二个例子演示的是方法引用,在方法已经存在且表示类执行的操作的情况下使用,这种语法可以代替正常的Lambda语法。最后的一个示例展示了printCustom方法在这种情况下也可以使用。请注意当Lambda表达式中包含另一个表达式时的变量名轻微变化。

你可以用这种方式对任何集合进行迭代。基本的结构与增强的for循环相似,但是在类内包含迭代机制带来了许多好处。

链和过滤器

除了循环遍历一个集合的内容,你还可以将这些方法链在一起.第一个方法可以看做是一个过滤器把Predicate接口作为参数.

下面的示例是遍历一个List经过过滤之后的结果集.

Test02Filter.java

public class Test02Filter {   
   public static void main(String[] args) {
 
     List<Person> pl = Person.createShortList();
     
     SearchCriteria search = SearchCriteria.getInstance();
     
     System.out.println("\n=== Western Pilot Phone List ===");
 
     pl.stream().filter(search.getCriteria("allPilots"))
       .forEach(Person::printWesternName);
     
    
     System.out.println("\n=== Eastern Draftee Phone List ===");
 
     pl.stream().filter(search.getCriteria("allDraftees"))
       .forEach(Person::printEasternName);
     
   }
 }

第一个和最后一个循环演示了 List 是如何通过查询条件来过滤的 . 下面是最后一个循环的输出 :
=== Eastern Draftee Phone List ===

Name: Baker Bob
Age: 21  Gender: MALE
EMail: bob.baker@example.com
Phone: 201-121-4678
Address: 44 4th St, Smallville, KS 12333

Name: Doe John
Age: 25  Gender: MALE
EMail: john.doe@example.com
Phone: 202-123-4678
Address: 33 3rd St, Smallville, KS 12333

越来越懒

这些特征都很有用,但为何在已经有了for循环的前提下还要把这些特性加入到集合类呢?通过把迭代功能集合到一个库中,它可以让java开发者能做到更好的代码优化.为了更好解释,我们需要了解下面的术语定义:

  • Laziness(惰性):在编程中,惰性是指只处理那些你想要处理的并且你需要处理的对象.在前面的例子中,最后的循环遍历是惰性的因为它只遍历了List过滤后剩余的两个Person对象.这样代码更高效因为最终的处理步骤只发生在被选择的对象上.
  • Eagerness(急切化):List的每个对象上都执行操作的代码被认为是"急切的".例如,一个增强的for循环遍历整个List只为处理两个对象,这样可以认为是更"急切的"途径.

通过把循环集成到Collections库中,当有机会的时候代码可以更好的优化成"惰性"操作."急切的"做法更具有意义的时候(例如计算求和或者平均值),"急切的"操作仍可以使用.这样更高效更灵活相对与总使用"急切"操作而言.

 

stream方法

在前面的代码示例中,我们注意到stream方法在过滤之前和循环开始的时候被调用.这个方法将一个Collection作为输入并返回一个java.util.stream.Stream接口作为输出.一个Stream代表一个元素集合,任何方法都可以链上(个人注解:类似于建造者模式的一种写法,你可以连续调用stream的各种方法)。在默认情况下,一旦元素被消耗它们将不能从stream再次获取到(个人注解:例如调用forEach方法)。因此,一个操作链只能在一个特定的Stream上执行一次。此外,Stream可以串行(默认情况下)或者并行这依赖于方法的调用。在这个章节最后有一个并行处理的Stream示例。

变化和结果

前面提到,一个Stream在使用之后被丢弃。因此,在集合里的元素是不能使用Stream来交换或者改变。但是如果你想保存这些经过链操作之后的元素该怎么办呢?你可以将它们保存到另一个新的集合中。下面的代码将展示如何做到。

Test03toList.java

public class Test03toList {   
   public static void main(String[] args) {
     
     List<Person> pl = Person.createShortList();
     
     SearchCriteria search = SearchCriteria.getInstance();
     
     // Make a new list after filtering.
     List<Person> pilotList = pl
             .stream()
             .filter(search.getCriteria("allPilots"))
             .collect(Collectors.toList());
     
     System.out.println("\n=== Western Pilot Phone List ===");
     pilotList.forEach(Person::printWesternName);
 
   }
 
 }

collect方法调用时用到一个参数Collectors类。这个Collectors类能够返回stream的结果的List或者Set。这个例子展示了stream的结果是如何分配到新的List中,并遍历这个List

使用map计算

map方法一般和filter使用.该方法从一个类中获取某一属性并使用它.下面的例子演示如何使用年龄来计算的.

Test04Map.java

public class Test04Map {
 
   public static void main(String[] args) {
     List<Person> pl = Person.createShortList();
     
     SearchCriteria search = SearchCriteria.getInstance();
     
     // Calc average age of pilots old style
     System.out.println("== Calc Old Style ==");
     int sum = 0;
     int count = 0;
     
     for (Person p:pl){
       if (p.getAge() >= 23 && p.getAge() <= 65 ){
         sum = sum + p.getAge();
         count++;
       }
     }
     
     long average = sum / count;
     System.out.println("Total Ages: " + sum);
     System.out.println("Average Age: " + average);
     
     
     // Get sum of ages
     System.out.println("\n== Calc New Style ==");
     long totalAge = pl
             .stream()
             .filter(search.getCriteria("allPilots"))
             .mapToInt(p -> p.getAge())
             .sum();
 
     // Get average of ages
     OptionalDouble averageAge = pl
             .parallelStream()
             .filter(search.getCriteria("allPilots"))
             .mapToDouble(p -> p.getAge())
             .average();
 
     System.out.println("Total Ages: " + totalAge);
     System.out.println("Average Age: " + averageAge.getAsDouble());    
     
   }
   
 }

这个类的输出如下:

== Calc Old Style ==
Total Ages: 150
Average Age: 37

== Calc New Style ==
Total Ages: 150
Average Age: 37.5

这个程序计算在列表中所有飞行员的平均年龄.第一个循环演示了以前的方式通过for循环来计算.第二个循环使用map方法从串行流中获取每个人的年龄.注意totalAge是个long型。map方法返回一个IntStream对象,它包含sum方法返回long型。

注意:为了第二次计算平均值,不需要再计算年龄的总和。这只是为了教学演示如何使用sum方法。

最后的循环从流(Stream)中计算平均年龄。注意parallelStream方法是用来获取一个并行流这样就可以并行计算数值了。当然返回值跟上面的例子也有点不同。

资源

点击打开链接

 

总结

     在这个教程中,你已经学习了如何使用:

  • Java中的匿名内部类
  • 在Java SE 8中Lambda表达式代替匿名内部类
  • Lambda表达式的正确语法
  • 使用Predicate接口在列表中进行搜索
  • Function接口来处理对象和处理类型不一致的对象
  • 在Java SE 8中Collections加入的新特征来支持Lambda表达式

### 光流法C++源代码解析与应用 #### 光流法原理 光流法是一种在计算机视觉领域中用于追踪视频序列中运动物体的方法。它基于亮度不变性假设,即场景中的点在时间上保持相同的灰度值,从而通过分析连续帧之间的像素变化来估计运动方向和速度。在数学上,光流场可以表示为像素位置和时间的一阶导数,即Ex、Ey(空间梯度)和Et(时间梯度),它们共同构成光流方程的基础。 #### C++实现细节 在给定的C++源代码片段中,`calculate`函数负责计算光流场。该函数接收一个图像缓冲区`buf`作为输入,并初始化了几个关键变量:`Ex`、`Ey`和`Et`分别代表沿x轴、y轴和时间轴的像素强度变化;`gray1`和`gray2`用于存储当前帧和前一帧的平均灰度值;`u`则表示计算出的光流矢量大小。 #### 图像处理流程 1. **初始化和预处理**:`memset`函数被用来清零`opticalflow`数组,它将保存计算出的光流数据。同时,`output`数组被填充为白色,这通常用于可视化结果。 2. **灰度计算**:对每一像素点进行处理,计算其灰度值。这里采用的是RGB通道平均值的计算方法,将每个像素的R、G、B值相加后除以3,得到一个近似灰度值。此步骤确保了计算过程的鲁棒性和效率。 3. **光流向量计算**:通过比较当前帧和前一帧的灰度值,计算出每个像素点的Ex、Ey和Et值。这里值得注意的是,光流向量的大小`u`是通过`Et`除以`sqrt(Ex^2 + Ey^2)`得到的,再乘以10进行量化处理,以减少计算复杂度。 4. **结果存储与阈值处理**:计算出的光流值被存储在`opticalflow`数组中。如果`u`的绝对值超过10,则认为该点存在显著运动,因此在`output`数组中将对应位置标记为黑色,形成运动区域的可视化效果。 5. **状态更新**:通过`memcpy`函数将当前帧复制到`prevframe`中,为下一次迭代做准备。 #### 扩展应用:Lukas-Kanade算法 除了上述基础的光流计算外,代码还提到了Lukas-Kanade算法的应用。这是一种更高级的光流计算方法,能够提供更精确的运动估计。在`ImgOpticalFlow`函数中,通过调用`cvCalcOpticalFlowLK`函数实现了这一算法,该函数接受前一帧和当前帧的灰度图,以及窗口大小等参数,返回像素级别的光流场信息。 在实际应用中,光流法常用于目标跟踪、运动检测、视频压缩等领域。通过深入理解和优化光流算法,可以进一步提升视频分析的准确性和实时性能。 光流法及其C++实现是计算机视觉领域的一个重要组成部分,通过对连续帧间像素变化的精细分析,能够有效捕捉和理解动态场景中的运动信息
微信小程序作为腾讯推出的一种轻型应用形式,因其便捷性与高效性,已广泛应用于日常生活中。以下为该平台的主要特性及配套资源说明: 特性方面: 操作便捷,即开即用:用户通过微信内搜索或扫描二维码即可直接使用,无需额外下载安装,减少了对手机存储空间的占用,也简化了使用流程。 多端兼容,统一开发:该平台支持在多种操作系统与设备上运行,开发者无需针对不同平台进行重复适配,可在一个统一的环境中完成开发工作。 功能丰富,接口完善:平台提供了多样化的API接口,便于开发者实现如支付功能、用户身份验证及消息通知等多样化需求。 社交整合,传播高效:小程序深度嵌入微信生态,能有效利用社交关系链,促进用户之间的互动与传播。 开发成本低,周期短:相比传统应用程序,小程序的开发投入更少,开发周期更短,有助于企业快速实现产品上线。 资源内容: “微信小程序-项目源码-原生开发框架-含效果截图示例”这一资料包,提供了完整的项目源码,并基于原生开发方式构建,确保了代码的稳定性与可维护性。内容涵盖项目结构、页面设计、功能模块等关键部分,配有详细说明与注释,便于使用者迅速理解并掌握开发方法。此外,还附有多个实际运行效果的截图,帮助用户直观了解功能实现情况,评估其在实际应用中的表现与价值。该资源适用于前端开发人员、技术爱好者及希望拓展业务的机构,具有较高的参考与使用价值。欢迎查阅,助力小程序开发实践。资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值