文化馆网站建设,seo数据分析哪些方面,wordpress 付费字段,带域名的网站打不开java8 streams多年来#xff0c;使用Java进行多范式编程已经成为可能#xff0c;它支持面向服务#xff0c;面向对象和面向方面的编程的混合。 带有lambda和java.util.stream.Stream类的Java 8是个好消息#xff0c;因为它使我们可以将功能性编程范例添加到混合中。 确实使用Java进行多范式编程已经成为可能它支持面向服务面向对象和面向方面的编程的混合。 带有lambda和java.util.stream.Stream类的Java 8是个好消息因为它使我们可以将功能性编程范例添加到混合中。 确实lambda周围有很多炒作。 但是改变我们的习惯和编写代码的方式是明智的选择而无需先了解可能隐患的危险吗 Java 8的Stream类很简洁因为它使您可以收集数据并将该数据上的多个功能调用链接在一起从而使代码整洁。 映射/归约算法是一个很好的例子您可以通过首先从复杂域中选择或修改数据并对其进行简化“映射”部分然后将其缩减为一个有用的值来收集数据并将其聚合。 以以下数据类为例用Groovy编写这样我就可以免费生成构造函数访问器哈希/等于和toString方法的代码 //Groovy
Immutable
class City {String nameListTemperature temperatures
}
Immutable
class Temperature {Date dateBigDecimal reading
} 我可以使用这些类在City对象列表中构造一些随机天气数据例如 private static final long ONE_DAY_MS 1000*60*60*24;
private static final Random RANDOM new Random();public static ListCity prepareData(int numCities, int numTemps) {ListCity cities new ArrayList();IntStream.range(0, numCities).forEach( i -cities.add(new City(generateName(), generateTemperatures(numTemps))));return cities;
}private static ListTemperature generateTemperatures(int numTemps) {ListTemperature temps new ArrayList();for(int i 0; i numTemps; i){long when System.currentTimeMillis();when ONE_DAY_MS*RANDOM.nextInt(365);Date d new Date(when);Temperature t new Temperature(d, new BigDecimal(RANDOM.nextDouble()));temps.add(t);}return temps;
}private static String generateName() {char[] chars new char[RANDOM.nextInt(5)5];for(int i 0; i chars.length; i){chars[i] (char)(RANDOM.nextInt(26) 65);}return new String(chars);
} 第7行使用同样来自Java 8的IntStream类来构造第8-13行进行迭代的范围从而将新的城市添加到第6行构建的列表中。第22-30行在随机的日期生成随机温度。 如果要计算所有城市在八月记录的平均温度则可以编写以下函数算法 Instant start Instant.now();
Double averageTemperature cities.stream().flatMap(c -c.getTemperatures().stream()
).filter(t - {LocalDate ld LocalDateTime.ofEpochSecond(t.getDate().getTime(), 0, ZoneOffset.UTC).toLocalDate();return ld.getMonth() Month.AUGUST;
}).map(t -t.getReading()
).collect(Collectors.averagingDouble(TestFilterMapReducePerformance::toDouble)
);Instant end Instant.now();
System.out.println(functional calculated in Duration.between(start, end) : averageTemperature); 第1行用于启动时钟。 然后代码在第2行从城市列表中创建一个流。然后我使用flatMap方法也在第2行通过创建所有温度的单个长列表来flatMap 并在第3行传递一个lambda该lambda返回每个以流的形式列出温度 flatMap方法可以将其附加在一起。 完成此操作后我将在第4行使用filter方法丢弃所有非8月份以来的数据。 然后我在第11行调用map方法将每个Temperature对象转换为一个 BigDecimal以及生成的流我在第13行使用了collect方法以及一个计算平均值的收集器。 第15行需要一个辅助函数来将BigDecimal实例转换为double 因为第14行使用double而不是 BigDecimal /** method to convert to double */
public static Double toDouble(BigDecimal a) {return a.doubleValue();
} 上面清单中的数字运算部分可以用命令式编写如下所示 BigDecimal total BigDecimal.ZERO;
int count 0;
for(City c : cities){for(Temperature t : c.getTemperatures()){LocalDate ld LocalDateTime.ofEpochSecond(t.getDate().getTime(), 0, ZoneOffset.UTC).toLocalDate();if(ld.getMonth() Month.AUGUST){total total.add(t.getReading());count;}}
}
double averageTemperature total.doubleValue() / count; 在命令式的命令式版本中我以不同的顺序进行映射过滤和归约但是结果是相同的。 您认为哪种风格功能性或命令性更快并且提高了多少 为了更准确地读取性能数据我需要多次运行算法以便热点编译器有时间进行预热。 以伪随机顺序多次运行算法我能够测量出以功能样式编写的代码平均大约需要0.93秒使用一千个城市每个城市的温度为一千使用英特尔笔记本电脑进行计算i5 2.40GHz 64位处理器4核。 以命令式风格编写的代码花费了0.70秒速度提高了25。 所以我问自己命令式代码是否总是比功能代码更快。 让我们尝试简单地计算8月记录的温度数。 功能代码如下所示 long count cities.stream().flatMap(c -c.getTemperatures().stream()
).filter(t - {LocalDate ld LocalDateTime.ofEpochSecond(t.getDate().getTime(), 0, ZoneOffset.UTC).toLocalDate();return ld.getMonth() Month.AUGUST;
}).count(); 功能代码涉及过滤然后调用count方法。 另外等效的命令性代码可能如下所示 long count 0;
for(City c : cities){for(Temperature t : c.getTemperatures()){LocalDate ld LocalDateTime.ofEpochSecond(t.getDate().getTime(), 0, ZoneOffset.UTC).toLocalDate();if(ld.getMonth() Month.AUGUST){count;}}
} 在此示例中运行的数据集与用于计算平均8月温度的数据集不同命令性代码的平均时间为1.80秒而功能代码的平均时间略短。 因此我们无法推断出功能性代码比命令性代码更快或更慢。 这实际上取决于用例。 有趣的是我们可以使用parallelStream()方法而不是stream()方法来使计算并行运行。 在计算平均温度的情况下使用并行流意味着计算平均时间为0.46秒而不是0.93秒。 并行计算温度需要0.90秒而不是连续1.80秒。 尝试编写命令式代码该命令将数据分割在内核之间分布计算并将结果汇总为一个平均温度这将需要大量工作 正是这是想要向Java 8中添加函数式编程的主要原因之一。它如何工作 拆分器和完成器用于在默认的ForkJoinPool中分发工作默认情况下该ForkJoinPool已优化为使用与内核一样多的线程。 从理论上讲只使用与内核一样多的线程就意味着不会浪费任何时间进行上下文切换但这取决于所完成的工作是否包含任何阻塞的I / O –这就是我在有关Scala的书中所讨论的。 在使用Java EE应用程序服务器时生成线程是一个有趣的主题因为严格来说不允许您生成线程。 但是由于创建并行流不会产生任何线程因此无需担心 在Java EE环境中使用并行流完全合法 您也可以使用地图/减少算法来计算8月的温度总数 int count cities.stream().map(c -c.getTemperatures().size()
).reduce(Integer::sum
).get(); 第1行从列表中创建流并使用第2行上的lambda将城市映射转换为城市的温度数。第3行通过使用总和将“温度数”流减少为单个值第4行上的Integer类的method。由于流可能不包含任何元素 reduce方法返回Optional 我们调用get方法获取总数。 我们可以安全地这样做因为我们知道城市中包含数据。 如果您正在使用可能为空的数据则可以调用orElse(T)方法该方法允许您指定默认值如果没有可用结果时使用。 就编写功能代码而言还有另一种编写此算法的方法 long count cities.stream().map(c -c.getTemperatures().stream().count()
).reduce(Long::sum
).get(); 使用上述方法第2行上的lambda通过将温度列表转换为蒸汽并调用count方法来count温度列表的大小。 就性能而言 这是获取列表大小的一种不好的方法。 在第一个算法中每个城市有1000个城市温度有1000个总计数在160毫秒内计算。 第二种算法将时间增加到280ms 原因是ArrayList知道其大小因为它在添加或删除元素时对其进行跟踪。 另一方面流首先通过将每个元素映射到值1L 然后使用Long::sum方法减少1L的流来计算大小。 在较长的数据列表上与仅从列表中的属性查找大小相比这是相当大的开销。 将功能代码所需的时间与以下命令代码所需的时间进行比较可以看出该功能代码的运行速度慢了一倍–命令代码计算的平均温度总数仅为80ms。 long count 0;
for(City c : cities){count c.getTemperatures().size();
} 通过使用并行流而不是顺序流再次通过简单地在上面三个清单中的第1行上调用parallelStream()方法而不是stream()方法结果是该算法平均需要90毫秒即比命令性代码略长。 计算温度的第三种方法是使用收集器 。 在这里我使用了一百万个城市每个城市只有两个温度。 该算法是 int count cities.stream().collect(Collectors.summingInt(c - c.getTemperatures().size())
); 等效的命令性代码为 long count 0;
for(City c : cities){count c.getTemperatures().size();
} 平均而言功能性列表花费了100毫秒这与命令性列表花费的时间相同。 另一方面使用并行流将计算时间减少了一半仅为50ms。 我问自己的下一个问题是是否有可能确定需要处理多少数据因此使用并行流值得吗 拆分数据将其提交给ForkJoinPool类的ExecutorService并在计算后将结果收集在一起并不是免费的-它会降低性能。 当可以并行处理数据时肯定可以计算出来通常的答案是这取决于用例。 在此实验中我计算了一个数字列表的平均值。 我NUM_RUNS地重复工作 NUM_RUNS次以获得可测量的值因为计算三个数字的平均值太快了无法可靠地进行测量。 我将列表的大小从3个数字更改为3百万个以确定列表需要多大才能使用并行流计算平均值才能得到回报。 使用的算法是 double avg -1.0;
for(int i 0; i NUM_RUNS; i){avg numbers.stream().collect(Collectors.averagingInt(n-n));
} 只是为了好玩这是另一种计算方法 double avg -1.0;
for(int i 0; i NUM_RUNS; i){avg numbers.stream().mapToInt(n-n).average().getAsDouble();
} 结果如下。 仅使用列表中的三个数字我就运行了100,000次计算。 多次运行测试表明平均而言串行计算花费了20ms而并行计算则花费了370ms。 因此在这种情况下使用少量数据样本不值得使用并行流。 另一方面列表中有300万个数字串行计算花费了1.58秒而并行计算仅花费了0.93秒。 因此在这种情况下对于大量数据样本值得使用并行流。 请注意随着数据集大小的增加运行次数减少了因此我不必等待很长的时间我不喝咖啡。 列表中的个数字 平均 时间序列 平均 时间平行 NUM_RUNS 3 0.02秒 0.37秒 100,000 30 0.02秒 0.46秒 100,000 300 0.07秒 0.53秒 100,000 3,000 1.98秒 2.76秒 100,000 30,000 0.67秒 1.90秒 10,000 30万 1.71秒 1.98秒 1,000 3,000,000 1.58秒 0.93秒 100 这是否意味着并行流仅对大型数据集有用 没有 这完全取决于手头的计算强度。 以下无效的算法只是加热CPU但演示了复杂的计算。 private void doIntensiveWork() {double a Math.PI;for(int i 0; i 100; i){for(int j 0; j 1000; j){for(int k 0; k 100; k){a Math.sqrt(a1);a * a;}}}System.out.println(a);
} 我们可以使用以下清单生成两个可运行对象的列表它们将完成这项繁重的工作 private ListRunnable generateRunnables() {Runnable r () - {doIntensiveWork();};return Arrays.asList(r, r);
} 最后我们可以测量运行两个可运行对象的时间例如并行运行请参见第3行对parallelStream()方法的调用 ListRunnable runnables generateRunnables();
Instant start Instant.now();
runnables.parallelStream().forEach(r - r.run());
Instant end Instant.now();
System.out.println(functional parallel calculated in Duration.between(start, end)); 使用并行流平均要花费260毫秒来完成两次密集的工作。 使用串行流平均花费460毫秒即几乎翻倍。 从所有这些实验中我们可以得出什么结论 好吧不可能最终说出功能代码比命令性代码慢也不能说使用并行流比使用串行流快。 我们可以得出的结论是程序员在编写对性能至关重要的代码时需要试验不同的解决方案并测量编码风格对性能的影响。 但是说实话这不是什么新鲜事 对我而言阅读本文后您应该带走的是总是有很多方法可以编写算法并且选择正确的方法很重要。 知道哪种方法是对的这是经验的结合但更重要的是尝试使用代码并尝试不同的解决方案。 最后尽管如此还是不要过早地进行优化 翻译自: https://www.javacodegeeks.com/2014/05/the-effects-of-programming-with-java-8-streams-on-algorithm-performance.htmljava8 streams