当前位置: 首页 > news >正文

郴州网站建设哪个好杂志媒体网站建设方案

郴州网站建设哪个好,杂志媒体网站建设方案,国内做的好网站有哪些,做一个网站和手机软件多少钱Lambda表达式 Java8新引入的语法糖 Lambda表达式*#xff08;关于lambda表达式是否属于语法糖存在很多争议#xff0c;有人说他并不是语法糖#xff0c;这里我们不纠结于字面表述#xff09;*。Lambda表达式是一种用于取代匿名类#xff0c;把函数行为表述为函数式编程风…Lambda表达式 Java8新引入的语法糖 Lambda表达式*关于lambda表达式是否属于语法糖存在很多争议有人说他并不是语法糖这里我们不纠结于字面表述*。Lambda表达式是一种用于取代匿名类把函数行为表述为函数式编程风格的一种匿名函数这里再重申一下Lambda表达式的执行结果是函数式接口的一个匿名对象Lambda表达式的基本语法在初级课程已经讲过在此不做赘述。 1. 示例代码 需求遍历List集合 1.1. 使用Lambda表达式 public class LambdaTest {public static void main(String... args) {ListString strList Arrays.asList(马, 士, 兵);strList.forEach(s - {System.out.println(s);});} }1.2. 使用匿名内部类 那么我们可以使用匿名内部类的形式来实现上述lambda表达式的功能以下代码的功能一致的 public class LambdaTest2 {public static void main(String... args) {ListString strList Arrays.asList(马, 士, 兵);//通过匿名内部类来代替lambda表达式strList.forEach(new ConsumerString() {Overridepublic void accept(String s) {System.out.println(s);}});} }2. 示例代码分析 forEach()是Iterable接口的一个默认方法它需要一个Consumer类型的参数方法体中是一个for循环对迭代器的每一个对象进行遍历处理方法就是调用参数对象的accept()方法 default void forEach(Consumer? super T action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}}继续查看Consumer的accept(T)方法可以看到Consumer是一个函数式接口只有一个抽象方法的接口java8中称之为函数式接口只有一个抽象方法accept(T)。 FunctionalInterface public interface ConsumerT {/*** Performs this operation on the given argument.** param t the input argument*/void accept(T t);// ... }我们对照一下示例代码 strList.forEach(s - {System.out.println(s);}); 可以大胆的猜测Lambda表达式 s - {System.out.println(s);}相当于是实现了Consumer接口的一个匿名内部类对象 而大括号里面的内容System.out.println(s);相当于重写了accept()的方法体。 当然事情远远没有这么简单想要直接看结论的童鞋请跳转本节最后。 3. 反编译lambda表达式代码 对包含lambda表达式的class文件进行反编译时需要注意 jad系列的反编译工具不支持jdk1.8所以这里使用CFR进行反编译。 cfr下载地址:http://www.benf.org/other/cfr/ 语法java -jar cfr-0.145.jar LambdaTest.class --decodelambdas false 反编译后可以得到 import java.io.PrintStream; import java.lang.invoke.LambdaMetafactory; import java.util.Arrays; import java.util.List; import java.util.function.Consumer;public class LambdaTest {public static void main(String ... args) {ListString strList Arrays.asList(马, 士, 兵);strList.forEach((ConsumerString)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());}private static /* synthetic */ void lambda$main$0(String s) {System.out.println(s);} }可以看到在forEach()方法中其实是调用了java.lang.invoke.LambdaMetafactory#metafactory()方法该方法的第5个参数implMethod指定了方法实现。可以看到这里其实是调用lambda$main$0()方法进行输出。跟踪metafactory()方法参数较多可以跳过 public static CallSite metafactory(// 调用者LambdaTest可访问权限的上下文对象JVM自动填充MethodHandles.Lookup caller, // 要执行的方法名即Consumer.accept()JVM自动填充String invokedName,// 调用点预期的签名包含目标方法参数类型String和Lambda返回类型ConsumerJVM自动填充MethodType invokedType,// 函数式接口抽象方法的签名 (Object)void泛型String被擦出所以是ObjectMethodType samMethodType,// 直接方法句柄真正被调用的方法即lambda$main$0签名为MethodHandle(String)voidMethodHandle implMethod,// 实例化的方法签名即调用时动态执行的方法签名// 可能与samMethodType相同也可能包含了泛型的具体类型比如(String)voidMethodType instantiatedMethodType) throws LambdaConversionException {AbstractValidatingLambdaMetafactory mf;mf new InnerClassLambdaMetafactory(caller, invokedType,invokedName, samMethodType,implMethod, instantiatedMethodType,false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);mf.validateMetafactoryArgs();return mf.buildCallSite();}其中new InnerClassLambdaMetafactory看起来是创建了一个Lambda相关的内部类继续跟踪下去 public InnerClassLambdaMetafactory(...)throws LambdaConversionException {//....lambdaClassName targetClass.getName().replace(., /) $$Lambda$ counter.incrementAndGet();cw new ClassWriter(ClassWriter.COMPUTE_MAXS);//.... }省略了一部分代码。 一看到lambdaClassName这样的变量名就知道它代表的什么意思——Lambda表达式对应的类名而ClassWriter对象cw暴露了Lambda表达式的底层实现机制ASM技术AssemblyJava字节码操作和分析框架用于在程序运行时动态生成和操作字节码文件。在这个构造方法里初始化了大量的ASM技术需要的成员变量为后续生成字节码的相关操作完成了一系列的初始化动作。现在大致可以猜测Lambda表达式底层是通过一个内部类来实现的这个类由ASM技术在程序运行时动态生成它实现了函数式接口例如Consumer等并重写了对应的抽象方法如accept。 4. 验证猜想 回到metafactory()方法中跟踪方法结尾的返回语句mf.buildCallSite();——创建调用点这才是重点 /*** Build the CallSite. Generate a class file which implements the functional* interface, define the class, if ...* 创建调用点。定义一个实现了函数式接口的类并生成它的类文件* return a CallSite, which, when invoked, will return an instance of the* functional interface* 返回一个调用点当它执行的时候将会返回一个函数式接口Consumer的实例*/OverrideCallSite buildCallSite() throws LambdaConversionException {final Class? innerClass spinInnerClass();// 省略部分代码...try {Object inst ctrs[0].newInstance();return new ConstantCallSite(MethodHandles.constant(samBase, inst));}// ...}方法的注释非常清晰的告诉我们这个方法在运行期会返回一个函数式接口的实例也就是Consumer接口的匿名对象。 方法体的第一行spinInnerClass()就使用ASM技术生成了一个Class文件然后使用sun.misc.Unsafe将该类加载到JVM创建并返回该类的Class对象 private final ClassWriter cw; // ASM class writer/*** Generate a class file which implements the functional* interface, define and return the class.* 生成一个实现函数式接口的类文件定义并返回该类的Class实例* return a Class which implements the functional interface* 返回一个实现函数式接口的Class实例*/private Class? spinInnerClass() throws LambdaConversionException {// ....// ClassWriter通过visit方法动态构造类的字节码cw.visit(, , lambdaClassName, null, , interfaces); // 生成接口字节码// ...for ( ; ; ) {cw.visitField( , , , null, null); // 生成域的字节码}generateConstructor(); // 生成构造器字节码// ...cw.visitMethod( , , , null, null); // 生成普通方法字节码// ...cw.visitEnd(); // end// Define the generated class in this VM.final byte[] classBytes cw.toByteArray();// If requested, dump out to a file for debugging purposesif (dumper ! null) { // 转储对象AccessController.doPrivileged(new PrivilegedActionVoid() {Overridepublic Void run() {dumper.dumpClass(lambdaClassName, classBytes);return null;}}, null,new FilePermission(ALL FILES, read, write),// createDirectories may need itnew PropertyPermission(user.dir, read));}// 使用Unsafe对象定义并返回该内部类字节码文件对象Classreturn UNSAFE.defineAnonymousClass(targetClass, classBytes, null);}这个方法的后半部分if (dumper ! null) 代码块给我们提供了将该内部类转储到本地磁盘用以调试的可能在LambdaTest的main方法里里添加一行代码将Lambda表达式对应的内部类转储到指定目录IDEA System.setProperty(jdk.internal.lambda.dumpProxyClasses, out/production/);程序运行之后会将Lambda表达式对应的内部类文件生成出来com.boxuegu.intermediate.language.sugar.lambda.LambdaTest$$Lambda$1 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MfeEy8xY-1678290472261)(D:/工作/03_资料/lambda表达式对应的内部类.png)] 反编译这个类代码如下 import java.lang.invoke.LambdaForm; import java.util.function.Consumer;final class LambdaTest$$Lambda$1 implements Consumer { // 实现函数式接口private LambdaTest$$Lambda$1() {}LambdaForm.Hiddenpublic void accept(Object object) { // 重写抽象方法LambdaTest.lambda$main$0((String)object);} }Bingo 现在我们初步得到了一些结论 Lambda表达式底层是用内部类来实现的该内部类实现了*某个根据Lambda所属的代码指定*函数式接口并重写了该接口的抽象方法该内部类是在程序运行时使用ASM技术动态生成的所以编译期没有对应的.class文件但是我们可以通过设置系统属性将该内部类文件转储出来 5. Lambda表达式编译和运行过程 至此我们只窥视了Lambda表达式底层实现的冰山一角。接下来会有一堆概念和过程慎入 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oA17iNOh-1678290472262)(D:/工作/03_资料/Lambda表达式编译和运行过程.png)] Java7在JSRJava Specification RequestsJava 规范提案 292中增加了对动态类型语言的支持使得Java也可以像C语言那样将方法作为参数传递其实现在java.lang.invoke包中。它的核心就是invokedynamic指令为后面函数式编程和响应式编程提供了前置支持。invokedynamic指令对应的执行方法会关联到一个动态调用点对象java.lang.invoke.CallSite一个调用点call site是一个方法句柄method handle调用点的目标的持有者这个调用点对象会指向一个具体的引导方法bootstrap method比如metafactory()引导方法成功调用之后调用点的目标将会与它持有的方法句柄的引用永久绑定最终得到一个实现了函数式接口比如Consumer的对象。Lambda表达式在编译期进行脱糖desugar它的主体部分会被转换成一个脱糖方法desugared method即lambda$main$0这是一个合成方法如果Lambda没有用到外部变量则是一个私有的静态方法否则将是个私有的实例方法——synthetic 表示不在源码中显示并在Lambda所属的方法比如main方法中生成invokedynamic指令。进入运行期invokedynamic指令会调用引导方法metafactory()初始化ASM生成内部类所需的各项属性然后由spinInnerClass()方法组装内部类并用Unsafe加载到JVM通过构造方法实例化内部类的实例Lambda的实现内部类的构造是私有的需要手动设置可访问属性为true最后绑定到方法句柄完成调用点的创建。你可以把调用点看成是函数式接口例如Consumer等的匿名对象当然内部类是确实存在的——比如final class LambdaTest$$Lambda$1 implements Consumer。值得注意的是内部类的实现方法里并没有Lambda表达式的任何操作它不过是调用了脱糖后定义在调用点目标类targetClass即LambdaTest类中的合成方法即lambda$main$0而已这样做使得内部类的代码量尽可能的减少降低内存占用对效率的提升更加稳定和可控。 6. Lambda表达式的语法糖结论 Lambda表达式在编译期脱去糖衣语法生成了一个“合成方法”在运行期invokedynamic指令通过引导方法创建调用点过程中生成一个实现了函数式接口的内部类并返回它的对象最终通过调用点所持有的方法句柄完成对合成方法的调用实现具体的功能。 Lambda表达式是一个语法糖但远远不止是一个语法糖。 方法引用 在使用Lambda表达式的时候我们实际上传递进去的代码就是一种解决方案拿什么参数做什么操作。那么考虑一种情况如果我们在Lambda中所指定的操作方案已经有地方存在相同方案那是否还有必要再写重复逻辑 1.冗余的Lambda场景 来看一个简单的函数式接口以应用Lambda表达式 FunctionalInterface public interface Printable{voidprint(Stringstr); }在Printable接口当中唯一的抽象方法print接收一个字符串参数目的就是为了打印显示它。那么通过Lambda来使用它的代码很简单 public class Demo01PrintSimple {private static void printString(Printable data) {data.print(Hello, World!);} public static void main(String[] args) {printString(s ‐ System.out.println(s));} }其中 printString 方法只管调用 Printable 接口的 print 方法而并不管 print 方法的具体实现逻辑会将字符串打印到什么地方去。而 main 方法通过Lambda表达式指定了函数式接口 Printable 的具体操作方案为拿到String类型可推导所以可省略数据后在控制台中输出它。 2. 问题分析 这段代码的问题在于对字符串进行控制台打印输出的操作方案明明已经有了现成的实现那就是 System.out对象中的 println(String) 方法。既然Lambda希望做的事情就是调用 println(String) 方法那何必自己手动调用呢 3. 用方法引用改进代码 能否省去Lambda的语法格式尽管它已经相当简洁呢只要“引用”过去就好了 public class Demo02PrintRef {private static void printString(Printable data) {data.print(Hello, World!);} public static void main(String[] args) {printString(System.out::println);} }请注意其中的双冒号 :: 写法这被称为“方法引用”而双冒号是一种新的语法。 4. 方法引用符 双冒号 :: 为引用运算符而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中那么则可以通过双冒号来引用该方法作为Lambda的替代者。 语义分析 例如上例中 System.out 对象中有一个重载的 println(String) 方法恰好就是我们所需要的。那么对于 printString 方法的函数式接口参数对比下面两种写法完全等效 Lambda表达式写法 s - System.out.println(s);方法引用写法 System.out::println 第一种语义是指拿到参数之后经Lambda之手继而传递给 System.out.println 方法去处理。 第二种等效写法的语义是指直接让 System.out 中的 println 方法来取代Lambda。两种写法的执行效果完全一样而第二种方法引用的写法复用了已有方案更加简洁。 注:Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常 推导与省略 如果使用Lambda那么根据“可推导就是可省略”的原则无需指定参数类型也无需指定的重载形式——它们都 将被自动推导。而如果使用方法引用也是同样可以根据上下文进行推导。 函数式接口是Lambda的基础而方法引用是Lambda的孪生兄弟。 下面这段代码将会调用 println 方法的不同重载形式将函数式接口改为int类型的参数 FunctionalInterface public interface PrintableInteger {void print(int str); }由于上下文变了之后可以自动推导出唯一对应的匹配重载所以方法引用没有任何变化 public class Demo03PrintOverload {private static void printInteger(PrintableInteger data) {data.print(1024);}public static void main(String[] args) {printInteger(System.out::println);} }这次方法引用将会自动匹配到 println(int) 的重载形式。 5. 通过对象名引用成员方法 这是最常见的一种用法与上例相同。如果一个类中已经存在了一个成员方法 public class MethodRefObject {public void printUpperCase(String str) {System.out.println(str.toUpperCase());} }函数式接口仍然定义为 FunctionalInterface public interface Printable {void print(String str); }那么当需要使用这个 printUpperCase 成员方法来替代 Printable 接口的Lambda的时候已经具有了 MethodRefObject 类的对象实例则可以通过对象名引用成员方法代码为 public class Demo04MethodRef {private static void printString(Printable lambda) {lambda.print(Hello);} public static void main(String[] args) {MethodRefObject obj new MethodRefObject();printString(obj::printUpperCase);} }6. 通过类名称引用静态方法 由于在 java.lang.Math 类中已经存在了静态方法 abs 所以当我们需要通过Lambda来调用该方法时有两种写法。首先是函数式接口 FunctionalInterface public interface Calcable {int calc(int num); } 第一种写法是使用Lambda表达式 public class Demo05Lambda {private static void method(int num, Calcable lambda) {System.out.println(lambda.calc(num));} public static void main(String[] args) {method(‐10, n ‐ Math.abs(n));} }但是使用方法引用的更好写法是 public class Demo06MethodRef {private static void method(int num, Calcable lambda) {System.out.println(lambda.calc(num));} public static void main(String[] args) {method(‐10, Math::abs);} } 在这个例子中下面两种写法是等效的 Lambda表达式 n - Math.abs(n)方法引用 Math::abs 7. 通过super引用成员方法 如果存在继承关系当Lambda中需要出现super调用时也可以使用方法引用进行替代。首先是函数式接口 FunctionalInterface public interface Greetable {void greet(); } 然后是父类 Human 的内容 public class Human {public void sayHello() {System.out.println(Hello!);} }最后是子类 Man 的内容其中使用了Lambda的写法 public class Man extends Human {Overridepublic void sayHello() {System.out.println(大家好,我是Man!);}// 定义方法method,参数传递Greetable接口public void method(Greetable g){g.greet();} public void show(){// 调用method方法,使用Lambda表达式method(()‐{// 创建Human对象,调用sayHello方法new Human().sayHello();});// 简化Lambdamethod(()‐new Human().sayHello());// 使用super关键字代替父类对象method(()‐super.sayHello());} } 但是如果使用方法引用来调用父类中的 sayHello 方法会更好例如另一个子类 Woman public class Man extends Human {Overridepublic void sayHello() {System.out.println(大家好,我是Man!);} // 定义方法method,参数传递Greetable接口public void method(Greetable g){g.greet();}public void show(){method(super::sayHello);} }在这个例子中下面两种写法是等效的 Lambda表达式 () - super.sayHello()方法引用 super::sayHello 8. 通过this引用成员方法 this代表当前对象如果需要引用的方法就是当前类中的成员方法那么可以使用 this::成员方法 的格式来使用方法引用。首先是简单的函数式接口 FunctionalInterface public interface Richable {void buy(); } 下面是一个丈夫 Husband 类 public class Husband {private void marry(Richable lambda) {lambda.buy();} public void beHappy() {marry(() ‐ System.out.println(买套房子));} } 开心方法 beHappy 调用了结婚方法 marry 后者的参数为函数式接口 Richable 所以需要一个Lambda表达式。但是如果这个Lambda表达式的内容已经在本类当中存在了则可以对 Husband 丈夫类进行修改 public class Husband {private void buyHouse() {System.out.println(买套房子);} private void marry(Richable lambda) {lambda.buy();} public void beHappy() {marry(() ‐ this.buyHouse());} }如果希望取消掉Lambda表达式用方法引用进行替换则更好的写法为 public class Husband {private void buyHouse() {System.out.println(买套房子);} private void marry(Richable lambda) {lambda.buy();} public void beHappy() {marry(this::buyHouse);} }在这个例子中下面两种写法是等效的 Lambda表达式 () - this.buyHouse()方法引用 this::buyHouse 9. 类的构造器引用 由于构造器的名称与类名完全一样并不固定。所以构造器引用使用 类名称::new 的格式表示。首先是一个简单 的 Person 类 public class Person {private String name;public Person(String name) {this.name name;} public String getName() {return name;} public void setName(String name) {this.name name;} } 然后是用来创建 Person 对象的函数式接口 public interface PersonBuilder {Person buildPerson(String name); }要使用这个函数式接口可以通过Lambda表达式 public class Demo09Lambda {public static void printName(String name, PersonBuilder builder) {System.out.println(builder.buildPerson(name).getName());} public static void main(String[] args) {printName(赵丽颖, name ‐ new Person(name));} } 但是通过构造器引用有更好的写法 public class Demo10ConstructorRef {public static void printName(String name, PersonBuilder builder) {System.out.println(builder.buildPerson(name).getName());} public static void main(String[] args) {printName(赵丽颖, Person::new);} } 在这个例子中下面两种写法是等效的 Lambda表达式 name - new Person(name)方法引用 Person::new 10. 数组的构造器引用 数组也是 Object 的子类对象所以同样具有构造器只是语法稍有不同。如果对应到Lambda的使用场景中时 需要一个函数式接口 FunctionalInterface public interface ArrayBuilder {int[] buildArray(int length); } 在应用该接口的时候可以通过Lambda表达式 public class Demo11ArrayInitRef {private static int[] initArray(int length, ArrayBuilder builder) {return builder.buildArray(length);} public static void main(String[] args) {int[] array initArray(10, length ‐ new int[length]);} }但是更好的写法是使用数组的构造器引用 public class Demo12ArrayInitRef {private static int[] initArray(int length, ArrayBuilder builder) {return builder.buildArray(length);} public static void main(String[] args) {int[] array initArray(10, int[]::new);} } 在这个例子中下面两种写法是等效的 Lambda表达式 length - new int[length] 方法引用 int[]::new Stream流 1.引言 说到Stream便容易想到I/O Stream而实际上谁规定“流”就一定是“IO流”呢在Java 8中得益于Lambda所带来的函数式编程引入了一个全新的Stream概念用于解决已有集合类库既有的弊端。 传统集合的多步遍历代码几乎所有的集合如 Collection 接口或 Map 接口等都支持直接或间接的遍历操作。而当我们需要对集合中的元素进行操作的时候除了必需的添加、删除、获取外最典型的就是集合遍历。例如 import java.util.ArrayList; import java.util.List; public class Demo01ForEach {public static void main(String[] args) {ListString list new ArrayList();list.add(张无忌);list.add(周芷若);list.add(赵敏);list.add(张强);list.add(张三丰);for (String name : list) {System.out.println(name);}} }这是一段非常简单的集合遍历操作对集合中的每一个字符串都进行打印输出操作。 循环遍历的弊端 Java 8的Lambda让我们可以更加专注于做什么What而不是怎么做How这点此前已经结合内部类进行了对比说明。现在我们仔细体会一下上例代码可以发现 for循环的语法就是“怎么做”for循环的循环体才是“做什么” 为什么使用循环因为要进行遍历。但循环是遍历的唯一方式吗遍历是指每一个元素逐一进行处理而并不是从第一个到最后一个顺次处理的循环。前者是目的后者是方式。 试想一下如果希望对集合中的元素进行筛选过滤 将集合A根据条件一过滤为子集B然后再根据条件二过滤为子集C。 那怎么办在Java 8之前的做法可能为 import java.util.ArrayList; import java.util.List; public class Demo02NormalFilter {public static void main(String[] args) {ListString list new ArrayList();list.add(张无忌);list.add(周芷若);list.add(赵敏);list.add(张强);list.add(张三丰);ListString zhangList new ArrayList();for (String name : list) {if (name.startsWith(张)) {zhangList.add(name);}}ListString shortList new ArrayList();for (String name : zhangList) {if (name.length() 3) {shortList.add(name);}}for (String name : shortList) {System.out.println(name);}} }这段代码中含有三个循环每一个作用不同 首先筛选所有姓张的人然后筛选名字有三个字的人最后进行对结果进行打印输出。 每当我们需要对集合中的元素进行操作的时候总是需要进行循环、循环、再循环。这是理所当然的么**不是。**循环是做事情的方式而不是目的。另一方面使用线性循环就意味着只能遍历一次。如果希望再次遍历只能再使用另一个循环从头开始。 那Lambda的衍生物Stream能给我们带来怎样更加优雅的写法呢 Stream的更优写法 下面来看一下借助Java 8的Stream API什么才叫优雅 import java.util.ArrayList; import java.util.List; public class Demo03StreamFilter {public static void main(String[] args) {ListString list new ArrayList();list.add(张无忌);list.add(周芷若);list.add(赵敏);list.add(张强);list.add(张三丰);list.stream().filter(s ‐ s.startsWith(张)).filter(s ‐ s.length() 3).forEach(System.out::println);} }直接阅读代码的字面意思即可完美展示无关逻辑方式的语义获取流、过滤姓张、过滤长度为3、逐一打印。代码中并没有体现使用线性循环或是其他任何算法进行遍历我们真正要做的事情内容被更好地体现在代码中。 2.流式思想概述 注意请暂时忘记对传统IO流的固有印象 **整体来看流式思想类似于工厂车间的“**生产流水线”。 当需要对多个元素进行操作特别是多步操作的时候考虑到性能及便利性我们应该首先拼好一个“模型”步骤 方案然后再按照方案去执行它。这是一种集合元素的处理方案而方案就是一种“函数模型”。 这里的 filter 、 map 、 skip 都是在对函数模型进行操作集合元素并没有真正被处理。只有当终结方法 count执行的时候整个模型才会按照指定策略执行操作。而这得益于Lambda的延迟执行特性。 备注“Stream流”其实是一个集合元素的函数模型它并不是集合也不是数据结构其本身并不存储任何元素或其地址值。 Stream流是一个来自数据源的元素队列 元素是特定类型的对象形成一个队列。 Java中的Stream并不会存储元素而是按需计算。数据源 流的来源。 可以是集合数组 等。 和以前的Collection操作不同 Stream操作还有两个基础的特征 Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道 如同流式风格fluent style。 这样做可以对操作进行优化 比如延迟执行(laziness)和短路( short-circuiting)。内部迭代 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代 这叫做外部迭代。 Stream提供了内部迭代的方式流可以直接调用遍历方法。 当使用一个流的时候通常包括三个基本步骤获取一个数据源source→ 数据转换→执行操作获取想要的结果每次转换原有 Stream 对象不改变返回一个新的 Stream 对象可以有多次转换这就允许对其操作可以像链条一样排列变成一个管道。 3.获取流 java.util.stream.StreamT 是Java 8新加入的最常用的流接口这并不是一个函数式接口。获取一个流非常简单有以下几种常用的方式 所有的 Collection 集合都可以通过 stream 默认方法获取流 Stream 接口的静态方法 of 可以获取数组对应的流。 1.根据Collection获取流 首先 java.util.Collection 接口中加入了default方法 stream 用来获取流所以其所有实现类均可获取流。 import java.util.*; import java.util.stream.Stream; public class Demo04GetStream {public static void main(String[] args) {ListString list new ArrayList();// ...StreamString stream1 list.stream();SetString set new HashSet();// ...StreamString stream2 set.stream();VectorString vector new Vector();// ...StreamString stream3 vector.stream();} } 2.根据Map获取流 java.util.Map 接口不是 Collection 的子接口且其K-V数据结构不符合流元素的单一特征所以获取对应的流需要分key、value或entry等情况 import java.util.HashMap; import java.util.Map; import java.util.stream.Stream; public class Demo05GetStream {public static void main(String[] args) {MapString, String map new HashMap();// ...StreamString keyStream map.keySet().stream();StreamString valueStream map.values().stream();StreamMap.EntryString, String entryStream map.entrySet().stream();} } 3.根据数组获取流 如果使用的不是集合或映射而是数组由于数组对象不可能添加默认方法所以 Stream 接口中提供了静态方法of 使用很简单 import java.util.stream.Stream; public class Demo06GetStream {public static void main(String[] args) {String[] array { 张无忌, 张翠山, 张三丰, 张一元 };StreamString stream Stream.of(array);} }备注 of 方法的参数其实是一个可变参数所以支持数组。 4.常用方法 流模型的操作很丰富这里介绍一些常用的API。这些方法可以被分成两种 延迟方法返回值类型仍然是 Stream 接口自身类型的方法因此支持链式调用。除了终结方法外其余方法均为延迟方法。终结方法返回值类型不再是 Stream 接口自身类型的方法因此不再支持类似 StringBuilder 那样的链式调用。本小节中终结方法包括 count 和 forEach 方法。 备注本小节之外的更多方法请自行参考API文档。 1.逐一处理forEach 虽然方法名字叫 forEach 但是与for循环中的“for-each”昵称不同。 void forEach(Consumer? super T action);该方法接收一个 Consumer 接口函数会将每一个流元素交给该函数进行处理。 复习Consumer接口 java.util.function.ConsumerT接口是一个消费型接口。Consumer接口中包含抽象方法void accept(T t)意为消费一个指定泛型的数据。基本使用 import java.util.stream.Stream; public class Demo12StreamForEach {public static void main(String[] args) {StreamString stream Stream.of(张无忌, 张三丰, 周芷若);stream.forEach(name‐ System.out.println(name));} } 2.过滤filter 可以通过 filter 方法将一个流转换成另一个子集流。方法签名 StreamT filter(Predicate? super T predicate);该接口接收一个 Predicate 函数式接口参数可以是一个Lambda或方法引用作为筛选条件。 复习Predicate接口 此前我们已经学习过 java.util.stream.Predicate 函数式接口其中唯一的抽象方法为 boolean test(T t);该方法将会产生一个boolean值结果代表指定的条件是否满足。如果结果为true那么Stream流的 filter 方法将会留用元素如果结果为false那么 filter 方法将会舍弃元素。 基本使用 Stream流中的 filter 方法基本使用的代码如 import java.util.stream.Stream; public class Demo07StreamFilter {public static void main(String[] args) {StreamString original Stream.of(张无忌, 张三丰, 周芷若);StreamString result original.filter(s ‐ s.startsWith(张));} } 在这里通过Lambda表达式来指定了筛选的条件必须姓张。 3.映射map 如果需要将流中的元素映射到另一个流中可以使用 map 方法。方法签名 R StreamR map(Function? super T, ? extends R mapper);该接口需要一个 Function 函数式接口参数可以将当前流中的T类型数据转换为另一种R类型的流。 复习Function接口 此前我们已经学习过 java.util.stream.Function 函数式接口其中唯一的抽象方法为 R apply(T t);这可以将一种T类型转换成为R类型而这种转换的动作就称为“映射”。 基本使用 Stream流中的 map 方法基本使用的代码如 import java.util.stream.Stream; public class Demo08StreamMap {public static void main(String[] args) {StreamString original Stream.of(10, 12, 18);StreamInteger result original.map(str‐Integer.parseInt(str));} } 这段代码中 map 方法的参数通过方法引用将字符串类型转换成为了int类型并自动装箱为 Integer 类对象。 4.统计个数count 正如旧集合 Collection 当中的 size 方法一样流提供 count 方法来数一数其中的元素个数 long count();该方法返回一个long值代表元素个数不再像旧集合那样是int值。基本使用 import java.util.stream.Stream; public class Demo09StreamCount {public static void main(String[] args) {StreamString original Stream.of(张无忌, 张三丰, 周芷若);StreamString result original.filter(s ‐ s.startsWith(张));System.out.println(result.count()); // 2} } 5.取用前几个limit limit 方法可以对流进行截取只取用前n个。方法签名 StreamT limit(long maxSize);参数是一个long型如果集合当前长度大于参数则进行截取否则不进行操作。基本使用 import java.util.stream.Stream; public class Demo10StreamLimit {public static void main(String[] args) {StreamString original Stream.of(张无忌, 张三丰, 周芷若);StreamString result original.limit(2);System.out.println(result.count()); // 2} } 6.跳过前几个skip 如果希望跳过前几个元素可以使用 skip 方法获取一个截取之后的新流 StreamT skip(long n);如果流的当前长度大于n则跳过前n个否则将会得到一个长度为0的空流。基本使用 import java.util.stream.Stream; public class Demo11StreamSkip {public static void main(String[] args) {StreamString original Stream.of(张无忌, 张三丰, 周芷若);StreamString result original.skip(2);System.out.println(result.count()); // 1} } 7.组合concat 如果有两个流希望合并成为一个流那么可以使用 Stream 接口的静态方法 concat static T StreamT concat(Stream? extends T a, Stream? extends T b)备注这是一个静态方法与 java.lang.String 当中的 concat 方法是不同的。 该方法的基本使用代码如 import java.util.stream.Stream; public class Demo12StreamConcat {public static void main(String[] args) {StreamString streamA Stream.of(张无忌);StreamString streamB Stream.of(张翠山);StreamString result Stream.concat(streamA, streamB);} }
文章转载自:
http://www.morning.tzrmp.cn.gov.cn.tzrmp.cn
http://www.morning.rqgbd.cn.gov.cn.rqgbd.cn
http://www.morning.jwbfj.cn.gov.cn.jwbfj.cn
http://www.morning.xhpnp.cn.gov.cn.xhpnp.cn
http://www.morning.pqchr.cn.gov.cn.pqchr.cn
http://www.morning.qbpqw.cn.gov.cn.qbpqw.cn
http://www.morning.tpxgm.cn.gov.cn.tpxgm.cn
http://www.morning.hwycs.cn.gov.cn.hwycs.cn
http://www.morning.xjwtq.cn.gov.cn.xjwtq.cn
http://www.morning.yrflh.cn.gov.cn.yrflh.cn
http://www.morning.nzzws.cn.gov.cn.nzzws.cn
http://www.morning.ytmx.cn.gov.cn.ytmx.cn
http://www.morning.grpfj.cn.gov.cn.grpfj.cn
http://www.morning.ylpl.cn.gov.cn.ylpl.cn
http://www.morning.srbbh.cn.gov.cn.srbbh.cn
http://www.morning.gl-group.cn.gov.cn.gl-group.cn
http://www.morning.rwzqn.cn.gov.cn.rwzqn.cn
http://www.morning.yqqxj26.cn.gov.cn.yqqxj26.cn
http://www.morning.ggmls.cn.gov.cn.ggmls.cn
http://www.morning.cpnlq.cn.gov.cn.cpnlq.cn
http://www.morning.rshijie.com.gov.cn.rshijie.com
http://www.morning.xwrhk.cn.gov.cn.xwrhk.cn
http://www.morning.qjxxc.cn.gov.cn.qjxxc.cn
http://www.morning.pqnps.cn.gov.cn.pqnps.cn
http://www.morning.fcwxs.cn.gov.cn.fcwxs.cn
http://www.morning.ywqw.cn.gov.cn.ywqw.cn
http://www.morning.dycbp.cn.gov.cn.dycbp.cn
http://www.morning.npgwb.cn.gov.cn.npgwb.cn
http://www.morning.lmpfk.cn.gov.cn.lmpfk.cn
http://www.morning.trtdg.cn.gov.cn.trtdg.cn
http://www.morning.tsynj.cn.gov.cn.tsynj.cn
http://www.morning.ygqjn.cn.gov.cn.ygqjn.cn
http://www.morning.krhkn.cn.gov.cn.krhkn.cn
http://www.morning.mplb.cn.gov.cn.mplb.cn
http://www.morning.wtdhm.cn.gov.cn.wtdhm.cn
http://www.morning.qcwrm.cn.gov.cn.qcwrm.cn
http://www.morning.qpfmh.cn.gov.cn.qpfmh.cn
http://www.morning.rrgm.cn.gov.cn.rrgm.cn
http://www.morning.kxxld.cn.gov.cn.kxxld.cn
http://www.morning.pmghz.cn.gov.cn.pmghz.cn
http://www.morning.tsycr.cn.gov.cn.tsycr.cn
http://www.morning.qyhcg.cn.gov.cn.qyhcg.cn
http://www.morning.gyqnc.cn.gov.cn.gyqnc.cn
http://www.morning.gqfks.cn.gov.cn.gqfks.cn
http://www.morning.dnmwl.cn.gov.cn.dnmwl.cn
http://www.morning.rfhm.cn.gov.cn.rfhm.cn
http://www.morning.cwjxg.cn.gov.cn.cwjxg.cn
http://www.morning.qttg.cn.gov.cn.qttg.cn
http://www.morning.wcczg.cn.gov.cn.wcczg.cn
http://www.morning.zkdbx.cn.gov.cn.zkdbx.cn
http://www.morning.qpntn.cn.gov.cn.qpntn.cn
http://www.morning.rnxw.cn.gov.cn.rnxw.cn
http://www.morning.tpchy.cn.gov.cn.tpchy.cn
http://www.morning.kwqwp.cn.gov.cn.kwqwp.cn
http://www.morning.czlzn.cn.gov.cn.czlzn.cn
http://www.morning.krdmn.cn.gov.cn.krdmn.cn
http://www.morning.rjtmg.cn.gov.cn.rjtmg.cn
http://www.morning.kspfq.cn.gov.cn.kspfq.cn
http://www.morning.knwry.cn.gov.cn.knwry.cn
http://www.morning.xkjrs.cn.gov.cn.xkjrs.cn
http://www.morning.rxlck.cn.gov.cn.rxlck.cn
http://www.morning.rgpbk.cn.gov.cn.rgpbk.cn
http://www.morning.qwpyf.cn.gov.cn.qwpyf.cn
http://www.morning.mbhdl.cn.gov.cn.mbhdl.cn
http://www.morning.rjznm.cn.gov.cn.rjznm.cn
http://www.morning.kzcfp.cn.gov.cn.kzcfp.cn
http://www.morning.kngqd.cn.gov.cn.kngqd.cn
http://www.morning.ntgjm.cn.gov.cn.ntgjm.cn
http://www.morning.nypsz.cn.gov.cn.nypsz.cn
http://www.morning.gbrps.cn.gov.cn.gbrps.cn
http://www.morning.dwmmf.cn.gov.cn.dwmmf.cn
http://www.morning.ylljn.cn.gov.cn.ylljn.cn
http://www.morning.rmfh.cn.gov.cn.rmfh.cn
http://www.morning.htbgz.cn.gov.cn.htbgz.cn
http://www.morning.xcxj.cn.gov.cn.xcxj.cn
http://www.morning.btns.cn.gov.cn.btns.cn
http://www.morning.psdsk.cn.gov.cn.psdsk.cn
http://www.morning.nyhtf.cn.gov.cn.nyhtf.cn
http://www.morning.thpns.cn.gov.cn.thpns.cn
http://www.morning.cthkh.cn.gov.cn.cthkh.cn
http://www.tj-hxxt.cn/news/277666.html

相关文章:

  • 好看的网站色彩搭配做网站一定要会ps么
  • 网站换代理网站建站公司哪家价钱合理
  • wordpress建站教程jiuyou营销型企业网站建设的功能
  • 什么网站做广告效果好百度文章收录提交入口
  • 网站设计需求模板网 公司
  • 桐柏网站建设网站建设 服务承诺
  • 高埗仿做网站做网站费用 会计分录
  • 网站水军怎么做免费微网站平台那个好
  • 精品网站免费宜昌建网站
  • 5 网站建设进度表公司建网站公司
  • 机关 网站 建设方案公司官网怎么注册流程
  • 网站推广效果的评估指标主要包括seo排名优化课程
  • 瑞金市网站建设推广网络营销案例
  • 旅游景点网站建设方案芜湖推广公司
  • 网站源码php网络规划设计师是副高
  • 上海云建站模板建设银行网站修改手机号
  • 专业论坛网站有哪些佛山市桂城建设局网站
  • 公司网站制作专业公司中级经济师考试科目
  • wordpress建哪些网站wordpress安装超时
  • 南阳网站seo推广公司织梦修改网站背景颜色
  • 做网站退款怎么做会计分录亿赐客网站
  • 建一个论坛网站怎么建齐鲁泰安人才网
  • 赣州网站建设怎么样外贸网站建设 双语网站建设
  • 音乐网站设计规划书广西住房城乡和建设厅网站首页
  • 网站建设柒首先金手指9医药招商网站大全免费
  • 棋牌类网站怎么做推广产品的方法
  • 演示网站怎么做乐清网站优化
  • 网站根据城市做二级目录小企业网站建设流程
  • 屏蔽ip网站设计制作中国第一架飞机的人是
  • 郑州做网站价格百度知道合伙人官网