Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 传统命令式编程与函数式编程的区别_Java_Performance_Java 8_Java Stream - Fatal编程技术网

Java 传统命令式编程与函数式编程的区别

Java 传统命令式编程与函数式编程的区别,java,performance,java-8,java-stream,Java,Performance,Java 8,Java Stream,我这里有一个问题陈述 我需要做的是遍历一个列表,找到第一个大于3的整数,然后将它加倍并返回它 以下是一些检查执行了多少操作的方法 public static boolean isGreaterThan3(int number){ System.out.println("WhyFunctional.isGreaterThan3 " + number); return number > 3; } public static boolean isE

我这里有一个问题陈述 我需要做的是遍历一个列表,找到第一个大于3的整数,然后将它加倍并返回它

以下是一些检查执行了多少操作的方法

public static boolean isGreaterThan3(int number){
        System.out.println("WhyFunctional.isGreaterThan3 " + number);
        return number > 3;
    }
    public static boolean isEven(int number){
        System.out.println("WhyFunctional.isEven " + number);
        return number % 2 == 0;
    }
    public static int doubleIt(int number){
        System.out.println("WhyFunctional.doubleIt " + number);
        return number << 1;
    }
总共8次手术

使用命令式风格或在java8之前,我可以像

for (Integer integer : integerList) {
            if(isGreaterThan3(integer)){
                if(isEven(integer)){
                    System.out.println(doubleIt(integer));
                    break;
                }
            }
        }
输出是

WhyFunctional.isGreaterThan3 1
WhyFunctional.isGreaterThan3 2
WhyFunctional.isGreaterThan3 3
WhyFunctional.isGreaterThan3 5
WhyFunctional.isEven 5
WhyFunctional.isGreaterThan3 4
WhyFunctional.isEven 4
WhyFunctional.doubleIt 4
Optional[8]
WhyFunctional.isGreaterThan3 1
WhyFunctional.isGreaterThan3 2
WhyFunctional.isGreaterThan3 3
WhyFunctional.isGreaterThan3 5
WhyFunctional.isEven 5
WhyFunctional.isGreaterThan3 4
WhyFunctional.isEven 4
WhyFunctional.doubleIt 4
8
和操作是一样的。所以我的问题是,如果我使用流而不是传统的for循环,它会有什么不同。

1)函数方法允许更具声明性的编程方式:您只需提供一个要应用的函数列表,而不需要手动编写迭代,因此您的代码有时更为一致


2) 如果您切换到parallel stream(),则可以自动将程序转换为parallel并更快地执行。这是可能的,因为您没有显式地对迭代进行编码,只列出要应用的函数,因此编译器/运行时可能会对其进行并行处理。

在这个简单的示例中,没有什么不同,JVM将尝试在每种情况下执行相同的工作量

在更复杂的示例中,您开始看到不同之处,如

integerList.parallelStream()
使循环的代码并发要困难得多。注意:您实际上不会这样做,因为开销会很高,您只需要第一个元素

顺便说一句,第一个示例返回结果,第二个打印。

命令式样式使用实现它的机制(迭代)完成计算逻辑。另一方面,函数式风格对两者进行了反编译。您根据提供逻辑的API编写代码,API可以自由选择应用它的方式和时间

特别是,Streams API有两种方法来应用逻辑:顺序或并行。后者实际上是将lambdas和streamsapi本身引入Java的驱动力

选择何时执行计算的自由导致了懒惰:而在命令式样式中,您有一个具体的数据集合,而在函数式样式中,您可以有一个集合与逻辑配对来转换它。当您实际使用数据时,可以“及时”应用该逻辑。这进一步允许您扩展计算的构建:每个方法都可以接收一个流并对其应用进一步的计算步骤,或者可以以不同的方式使用它(通过收集到一个列表中,通过只查找第一项而不对其余项应用计算,而是计算聚合值,等等)


作为laziness提供的新机会的一个特殊示例,我能够编写一个Spring MVC控制器,它返回一个
,其数据源是一个数据库,在我返回流时,数据仍然在数据库中。只有视图层会提取数据,隐式地应用它不知道的转换逻辑,永远不必在内存中保留超过一个流元素。这将一个典型的具有O(n)空间复杂性的解决方案转换为O(1),从而对结果集的大小变得不敏感。

流API引入了流的新思想,允许您以新的方式解耦任务。例如,根据您的任务,您可能希望使用大于3的双倍偶数做不同的事情。在某些地方你想找到第一个,在另一个地方你需要10个这样的数字,在第三个地方你想应用更多的过滤。您可以将查找此类数字的算法封装如下:

static IntStream numbers() {
    return IntStream.range(1, Integer.MAX_VALUE)
                    .filter(WhyFunctional::isGreaterThan3)
                    .filter(WhyFunctional::isEven)
                    .map(WhyFunctional::doubleIt);
}
给你。您刚刚创建了一个算法来生成这样的数字(不生成它们),您不关心它们将如何使用。一个用户可能会呼叫:

int num = numbers().findFirst().get();
其他用户可能需要获得10个这样的数字:

int[] tenNumbers = numbers().limit(10).toArray();
第三个用户可能希望找到第一个匹配的数字,该数字也可以被7整除:

int result = numbers().filter(n -> n % 7 == 0).findFirst().get();
用传统的命令式风格封装算法会更加困难


一般来说,流API与性能无关(尽管并行流可能比传统解决方案工作得更快)。这是关于代码的表达能力。

使用
API,您描述的是一个操作,而不是实现它。让
API实现操作的一个众所周知的优点是可以选择使用不同的执行策略,比如并行执行(正如其他人已经说过的)

另一个似乎有点被低估的特性是,有可能以命令式编程风格无法实现的方式更改操作本身,因为这意味着修改代码:

IntStream is=IntStream.rangeClosed(1, 10).filter(i -> i > 4);
if(evenOnly) is=is.filter(i -> (i&1)==0);
if(doubleIt) is=is.map(i -> i<<1);
is.findFirst().ifPresent(System.out::println);
IntStream is=IntStream.rangeClosed(1,10).filter(i->i>4);
如果(evenOnly)is=is.filter(i->(i&1)==0);
如果(doubleIt)is=is.map(i->issystem.out.println(k+“=>”+v));
由于
flatMap
相当于嵌套循环,因此以命令式样式编码相同的循环不再那么简单,因为我们有一个简单循环或基于运行时值的嵌套循环。通常,如果要在两种循环之间共享代码,就必须将代码拆分为多个方法


我已经遇到了一个实际示例,其中复杂操作的组合有多个条件
flatMap
步骤。等价的命令式代码是疯狂的…

传统的方法描述了您的操作方式,声明式描述了您需要的结果。@TimBiegeleisen,您错了。字节码非常非常不同。这一定是因为函数方法不同,必须有调用的动态指令,因为我们在这里使用lamda。@SashaSalauyou“how/what”二分法只是关于描述的级别:即使使用FP,您也会说如何实现更高的目标。例如,what=“寻找最佳业务前景”,how=“fi
IntStream is=IntStream.rangeClosed(1, 10).filter(i -> i > 4);
if(evenOnly) is=is.filter(i -> (i&1)==0);
if(doubleIt) is=is.map(i -> i<<1);
is.findFirst().ifPresent(System.out::println);
Stream<String> s = Stream.of("java8 streams", "are cool");
if(singleWords) s=s.flatMap(Pattern.compile("\\s")::splitAsStream);
s.collect(Collectors.groupingBy(str->str.charAt(0)))
 .forEach((k,v)->System.out.println(k+" => "+v));