模式和良好做法

来源:互联网 发布:单片机 auxr 1t模式 编辑:程序博客网 时间:2024/05/16 19:55


新的、优雅的方式提供数据处理管道处理案例的方法

许多事情已经介绍了java SE 8带来深刻的变化,我们写的应用程序和API的方式。这是lambda表达式,这样当然也为流和收藏家的API。Lambda表达式引起的变化方式的接口工作在java中,将默认的静态方法。有了这些新的工具,集合框架已经出现在几乎所有的接口的重要补充。

另一个元素被引入:最终类可选。此元素变化的方式我们可以写数据处理管道建立更好和更流畅的代码流。这篇文章的目的是给一个“可选的概念的细节,”以显示新的和优雅的方式结合带给我们的工具箱。结合能与流非常有效地工作,我们会看到。

什么是可选的?

一个可选的概念不是新的,已经在多种语言的实现。这个概念是很有帮助的事实,当建模方法调用返回一个未知的值或值不存在。

大多数时间,方法返回一个默认值。这是设计选择了最常。例如,一个电话地图。得到(关键)退货当钥匙不在映射。这是如何在集合框架工作的地图,即使这听起来可疑行为。事实上,这种方法不能用来判断一个给定的关键是在一个地图或不存在。事实上,如果此键已与空值相关(这当然不是你应该做的事情!),调用地图。得到(关键)也将返回null

判断是否有键在地图的正确方法是把下面的代码:

map.containskey(key);

这个可选类可以返回的get()地图的方法,即使它可能是经常检查返回的对象在每个电话内容繁琐。也就是说,它会认为角案件的处理是唯一的作用是错误的可选。事实上,我们已处理案例没有结合在java 20年。

这类被设计的方式带来了新的和优雅的方式,随着流带图案的API,提供在数据处理管道处理案例的新途径。

我们如何构建可选项吗?

从技术的角度来看,可选与私有构造函数的最后一个班。为了建立一个可选的,我们需要使用一个工厂方法提供。

第一个是of()可选。作为一个参数,它需要一个对象,不应为空。如果我们传递一个空的对象,这个方法,我们会得到一个NullPointerException。这种方法可以用来包装任何非‐空对象可选。

第二工厂方法ofnullable()可选。。这种方法也需要一个对象作为参数,所不同的是,这个对象可以是空的。但不要指望得到一个包裹在一个可选的空:如果你传递一个空的反对这种方法,你会得到一个空的可选。

所以没有办法创造一个可选包空对象。一个可选的是一个包装,持有非‐空对象或是空的。

为什么我们需要结合?

”这一概念的结果,不存在”是用在许多情况下。让我们举一个很简单的例子,基于流的API。让我们计算最大使用流:

IntStream stream = Stream.range(‐100, 100);OptionalInt opt = stream.max(Comparator.naturalOrder());

max()方法返回一个结果被包裹在一个optionalint。让我们探讨了如果是int错了什么

一个问题是如果流上我们计算这个最大原来是空的。记住,一个真正的‐生活流可以是一个复杂的计算,结果的映射,过滤,选择,等等。它也可以是一个主流的分裂导致码流分布式CPU并行计算中的几个核心之间。所以,空流是绝对可以发生,即使我们不希望它。

让我们写的代码要在分裂的情况下执行。让我们假设max()方法返回一个int而不是一个可选的

// This part of the code is run on CPU‐1 IntStream stream1 = Stream.range(‐100, 0);int max1 = stream1.max(Comparator.naturalOrder()); // max1 = ‐1// And this part on CPU‐2IntStream stream2 = Stream.range(0, 100);int max2 = stream2.max(Comparator.naturalOrder()); // max2 = 99 int max = Integer.max(max1, max2);

一切都好,因为我们的群并不是空的。

现在我们有一个错误的情况看,在一个流的原来是空的。

IntStream stream1 = Stream.range(‐100, 0);int max1 = stream1.max(Comparator.naturalOrder()); // max1 = ‐1IntStream stream2 = Stream.empty();int max2 = stream2.max(Comparator.naturalOrder()); // Suppose max2 = 0 int max = Integer.max(max1, max2); // result is 0

我们可以看到,选择int在返回类型max()方法将导致一个错误的结果,空流。原因是:返回的值在一个空的流进行操作应该是经营单位元。自max()操作没有标识的元素,这个返回值不存在。选择回归0导致错误的结果。

你可能会问:怎么回integer.min _值?这的确是一个可能性,但我们需要绝对肯定这int不会被转换成一个。这一招已在intsummarystatistics从JDK类。但可以肯定不能用在一般情况下。

可选来救援

一个空的设置最大的未定义,并选择一个通用的默认值是危险的。它可能导致腐败的结果在我们的数据处理管道。MAX是不是唯一的操作,没有身份的元素;这也为最小或平均的情况。

可选的类型已被引入到妥善处理这些案件。它的模型值可能不存在的事实。一个值,就不可能存在不同的值是零,或等于0,或是任何其他你能想到的默认值。

可选项:第一模式

有,事实上,两种曝光模式可选类。第一次看到一个可选的对象作为包装对象,就像整数,等。不同的是,有可能是在这个包装没有价值。

有几个方法来处理一个可选的作为包装:ispresent()get()

Optional<Person> opt = ...;if (opt.isPresent()) {   int value = opt.get(); // there is a value} else {   // decide what to do}

这一模式是简单和容易理解。如果我们看到有包装没有价值,我们可以决定默认值或跟随比主要的另一个模式。这是当get()方法是没有价值的,一个可选的NoSuchElementException抛出

然后我们都为这一模式的变体。我们可以使用orelse()使用默认值

Optional<Person> opt = ...;Person result = opt.orElse(Person.DEFAULT_PERSON);

在这种情况下,如果选项是空的,默认的人会回来。

这种模式是很好的只要对象person.default _人已经建成的,或是不建太贵。如果还没有建立,或者是在我们的应用程序性能的关键,那么我们可能不想使用这种模式。

我们可以使用此二变:

Optional<Person> opt = ...;Person result = opt.orElseGet(() ‐> Person.DEFAULT_PERSON);

在这种情况下,而不是通过一个完全建对象,我们通过一个供应商,将该对象是否需要建造。供应商是一个功能接口,模型的函数不带任何参数并返回一个值。

如果我们想抛出异常,我们可以用这第四个变种:

Optional<Person> opt = ...;Person result = opt.orElseThrow(() ‐> new MyCustomException());

在这种情况下,破例将建如果需要,并引发orelsethrow()方法

这一家族的图案很经典:我们检查是否有我们可选的东西。如果我们发现这个选项是空的,然后我们决定是返回一个默认值或抛出一个异常。作为奖励,感谢Lambda表达式的介绍,我们可以提供这个默认值或异常的构造函数,在供应商的形式。

可选项:二模式

但我们可以做的更好!

通过暴露的方法检查可选类,我们可以看到有一个家庭的方法,我们在同一界面map()filter(),和flatmap()-ifpresent(),它看起来像foreach()方法因为没有一个可选的多个元素,它不会调用此方法的道理foreach()

让我们看看如何利用这些方法。为此,我们使用从一个例子java SE 8的真的不耐烦礁Horstmann书(艾迪生卫斯理,2014)。

我们都知道,它是不可能计算0的平方根一个负数的逆。为了处理这些未定义的数学表达式是介绍一个特殊的数量称为南是什么(非数字)。

如何使用可选项而不是这把戏?让我们建立一个optionalmath类将返回其操作可选项。如果给定的操作可以通过参数计算,将结果放在可选。如果没有,返回的选择将是空的。这optionalmath类如下:

public class OptionalMath {   public static Optional<Double> sqrt(Double d) {    return d >= 0 ? Optional.of(Math.sqrt(d)):                      Optional.empty();   }   public static Optional<Double> inv(Double d) {    return d != 0 ? Optional.of(1/d):                      Optional.empty();   }}

这里的想法是很简单的。我们总是返回相同的对象类型,没有异常抛出。

假设我们有一个流的双打的过程。

一个平方根的倒数计算,第一个版本

第一(不太好)模式我们可以写如下:

doubles[] doubles = ...; // the doubles we need to processList<Double> result = new ArraysList<>(); // the result of our processingArrays.stream(doubles).boxed()   .forEach(      d ‐> OptionalMath.sqrt(d)                           .flatMap(OptionalMath::inv)                           .ifPresent(result::add));

首先,我们可以注意到,感谢flatmap()可选。方法,我们可以链调用逆平方根运算。

其次,该ifpresent()方法允许我们链添加结果到结果列表,不管是否有结果。最终的模式是很干净很光滑,没有杂乱如果‐然后‐别的没有异常处理。无法处理的只是自然地去除流值。

唯一的问题是,我们的lambda表达式是变异的外部列表:结果。这不是那么糟糕,会像我们期望的那样。然而,它是一种性能损失:访问外围环境应避免λ。

有一个隐藏的性能损失。因为我们是变异的外部ArrayList,将并行计算是不可能的。我们缺少一个好的优化机会。

一个平方根的倒数计算,第二版

可以用这个写真正的流处理代码optionalmath使用模式,不会发生变异,外部列表类,这是必须避免的?事实上,答案是肯定的,但我们需要重新考虑我们的过程我们的数据。

处理该流的自然的方式是将它写在这种方式:

Stream<Optional<Double>> intermediateResult = Arrays.stream(doubles).boxed()   .map(d ‐> OptionalMath.inv(d).flatMap(OptionalMath::sqrt);

问题是,清单的结合是不是一个好主意;我们喜欢收集名单的结果直接。我们需要一种方法来映射这个流的自选一个价值流。如果一个可选的从流是空的,然后,静静地,不加任何价值流。

所以我们需要一个函数,需要一双和返回一个流而不是可选。如果这个可选,流为空是空的,它会把价值包裹在这如果有一。

这可以通过以下功能:

Function<Double, Stream<Double>> invSqrt =   d ‐> OptionalMath.inv(d).flatMap(OptionalMath::sqrt)                       .map(result ‐> Stream.of(result))                       .orElseGet(() ‐> Stream.empty());

这个函数可以写的使用方法参考一个更优雅的方式:

Function<Double, Stream<Double>> invSqrt =   d ‐> OptionalMath.inv(d).flatMap(OptionalMath::sqrt)                      .map(Stream::of)                      .orElseGet(Stream::empty);

这种作用如何?好的,首先它需要计算:计算平方根的倒数和一个可选的返回结果。

然后,如果有一个结果(的地图方法是从可选类),它将导致流。这叫map()返回一个Optional<Stream<Double>>。如果上一步返回一个空的可选,这地图不做任何事情,返回一个空的可选。

最后一步打开这个选项。如果它有价值,包裹在一个流的orelse()打电话只是返回流,包装的结果值。如果没有价值,它返回所提供的空流。所以一个空的可选的转换这一步空流。

这个功能也正是我们所需要的:它需要一双和返回一个流与这双平方根成反比。如果此操作不能做因为这双是负的或零,函数将返回一个空流。

所以现在我们可以重写我们的数据处理管道在一个真正的“streamish”方式!

doubles[] doubles = ...; // the doubles we need to processList<Double> result = Arrays.stream(doubles).boxed()                  .flatMap(invSqrt)                  .collect(Collectors.toList());

没有突变的外部列表;这流可以与好的性能提升,并行计算!

结论

使用可选的第二种方法是比第一个更有趣。一个可选的可以被看作是一个流,要么是空的或单。这导致了非常自然流畅的图案,可以并行计算。所有的错误的价值观自然去除流,没有如果‐然后‐别的,处理也不例外,在双打的处理情况没有南。这种模式依赖于一个特殊的功能,可能看起来沉闷:一转换一个可选流。我们将在java SE 9有一个新的功能,stream()可选。会做这种转换直接。我们的invsqrt功能,因此,是这样写的java SE 9:

Function<Double, Stream<Double>> invSqrt =   d ‐> OptionalMath.inv(d).flatMap(OptionalMath::sqrt).stream();

参见

  • 这个可选在文档类
  • “可选的设计“
0 0
原创粉丝点击