25、stream api、自定义注解
一、什么是Stream API
A sequence of elements supporting sequential and parallel
aggregate operations
Stream是一组用来处理数组、集合的API
▪ Java 8之所以费这么大功夫引入函数式编程,原因有二:
– 代码简洁函数式编程写出的代码简洁且意图明确,使用stream接口让你从
此告别for循环。
– 多核友好,Java函数式编程使得编写并行程序从未如此简单,你需要的全部
就是调用一下parallel()方法。
二、Stream特性
1:不是数据结构,没有内部存储
2:不支持索引访问
3:延迟计算
4:支持并行
5:很容易生成数组或集合(List,Set)
6:支持过滤,查找,转换,汇总,聚合等操作
三、Stream运行机制
Stream分为 源source,中间操作,终止操作
流的源可以是一个数组、一个集合、一个生成器方法,一个I/O通道等等。
一个流可以有零个和或者多个中间操作,每一个中间操作都会返回
一个新的流,供下一个操作使用。一个流只会有一个终止操作
Stream只有遇到终止操作,它的源才开始执行遍历操作
四、Stream的创建
1、通过数组
2、通过集合来
3、通过Stream.generate方法来创建
4、通过Stream.iterate方法来创建
5、其他API创建
public class StreamDemo { //通过数组来生成 static void gen1(){ String[] strs = {"a","b","c","d"}; Stream<String> strs1 = Stream.of(strs); strs1.forEach(System.out::println); } //通过集合来生成 static void gen2(){ List<String> list = Arrays.asList("1","2","3","4"); Stream<String> stream = list.stream(); stream.forEach(System.out::println); } //generate static void gen3(){ Stream<Integer> generate = Stream.generate(() -> 1); // generate(Supplier<T> s)返回无限顺序无序流, 其中每个元素由提供的Suppliter generate.limit(5).forEach(System.out::println); // 没有limit 程序就不会中断 无限的 } //使用iterator static void gen4() { Stream<Integer> iterate = Stream.iterate(1, x -> x + 1); iterate.limit(5).forEach(System.out::println); } //其他方式 static void gen5(){ String str = "abcd"; IntStream stream =str.chars(); stream.forEach(System.out::println); } public static void main(String[] args) { //stream生成的操作 gen1(); gen2(); gen3(); gen4(); gen5(); } }
打印结果为:
/* a b c d 1 2 3 4 1 1 1 1 2 3 97 98 99 100 Process finished with exit code 0 */
五、Stream常用API
中间操作
过滤 filter
去重 distinct
排序 sorted
截取 limit、skip
转换 map/flatMap
其他 peek
终止操作
循环 forEach
计算 min、max、count、 average
匹配 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny
汇聚 reduce
收集器 toArray collect
示例1:
public class StreamDemo { public static void main(String[] args) {//中间操作:如果调用方法之后返回的结果是Stream对象就意味着是一个中间操作 Arrays.asList(1,2,3,4,5).stream().filter((x)->x%2==0).forEach(System.out::println); //求出结果集中所有偶数的和 int count = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream().filter(x -> x % 2 == 0).mapToInt(x->x).sum(); System.out.println(count); //求集合中的最大值 List<Integer> list = Arrays.asList(1,2, 3,4, 5, 6); Optional<Integer> max = list.stream().max((a, b) -> a - b); System.out.println(max.get()); //求集合的最小值 System.out.println(list.stream().min((a, b) -> a-b).get()); Optional<Integer> any = list.stream().filter(x -> x % 2 == 0).findAny(); // 任意取一个 System.out.println(any.get()); Optional<Integer> first = list.stream().filter(x -> x % 10 == 6).findFirst(); System.out.println(first.get()); Stream<Integer> integerStream = list.stream().filter(i -> { System.out.println("运行代码"); return i % 2 == 0; }); System.out.println(integerStream.findAny().get()); } }
打印结果为:
/* 2 4 20 6 1 2 6 运行代码 运行代码 2 */
为什么会打印两次 “运行代码”
把每一个元素分别取出来,执行中间操作到终止操作
1 拿出来 中间操作 打印一次 “运行代码”,
2 拿出来 中间操作 打印一次 “运行代码”,
满足条件,findAny终止操作,不再继续向下
如果是
List<Integer> list = Arrays.asList(1, 3, 5, 6);
呢, 打印几次?
结果是四次
6满足条件终止操作
相当于流水线,拿出1个 走完全部过程 走filter、FindAny
示例2:
public class StreamDemo { public static void main(String[] args) { List<Integer> list = Arrays.asList(1,2, 3,4, 5, 6); //获取最大值和最小值但是不使用min和max方法 Optional<Integer> min = list.stream().sorted().findFirst(); System.out.println(min.get()); Optional<Integer> max2 = list.stream().sorted((a, b) -> b - a).findFirst(); System.out.println(max2.get()); Arrays.asList("java","c#","python","scala").stream().sorted().forEach(System.out::println); Arrays.asList("java","c#","python","scala").stream().sorted((a,b)->a.length()-b.length()).forEach(System.out::println); } }
打印结果为:
/* 1 6 c# java python scala c# java scala python Process finished with exit code 0 */
示例3:
public class StreamDemo { public static void main(String[] args) { List<Integer> list = Arrays.asList(1,2, 3,4, 5, 6); //想将集合中的元素进行过滤同时返回一个集合对象 List<Integer> collect = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList()); collect.forEach(System.out::println); } }
打印结果为:
/* 2 4 6 Process finished with exit code 0 */
示例4:
public class StreamDemo { public static void main(String[] args) { List<Integer> list = Arrays.asList(1,2, 3,4, 5, 6); //去重操作 Arrays.asList(1,2,3,3,3,4,5,2).stream().distinct().forEach(System.out::println); System.out.println("---------------"); Arrays.asList(1,2,3,3,3,4,5,2).stream().collect(Collectors.toSet()).forEach(System.out::println); } }
示例5:
public class StreamDemo { public static void main(String[] args) { //打印20-30这样的集合数据 Stream.iterate(1,x->x+1).limit(50).skip(20).limit(10).forEach(System.out::println); // skip跳过20个输出10个 String str ="11,22,33,44,55"; // 求和 System.out.println(Stream.of(str.split(",")).mapToInt(x -> Integer.valueOf(x)).sum()); System.out.println(Stream.of(str.split(",")).mapToInt(Integer::valueOf).sum()); System.out.println(Stream.of(str.split(",")).map(x -> Integer.valueOf(x)).mapToInt(x -> x).sum()); System.out.println(Stream.of(str.split(",")).map(Integer::valueOf).mapToInt(x -> x).sum()); } }
打印结果为:
/* 21 22 23 24 25 26 27 28 29 30 165 165 165 165 Process finished with exit code 0 */
示例6:
public class StreamDemo { public static void main(String[] args) { //创建一组自定义对象 String str2 = "java,scala,python"; Stream.of(str2.split(",")).map(x->new com.mashibing.stream.Person(x)).forEach(System.out::println); Stream.of(str2.split(",")).map(com.mashibing.stream.Person::new).forEach(System.out::println); Stream.of(str2.split(",")).map(x-> com.mashibing.stream.Person.build(x)).forEach(System.out::println); Stream.of(str2.split(",")).map(com.mashibing.stream.Person::build).forEach(System.out::println); // 静态方法的方式 } }
public class Person { private String name; public Person() { }
public Person(String name) { this.name = name; } public static Person build(String name){ Person p = new Person(); p.setName(name); return p; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Deprecated public static void show(){ System.out.println("show"); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } public static void main(String[] args) { Person p = new Person(); show(); Date date = new Date(); System.out.println(date.getMinutes()); } }
打印结果为:
/* Person{name='java'} Person{name='scala'} Person{name='python'} Person{name='java'} Person{name='scala'} Person{name='python'} Person{name='java'} Person{name='scala'} Person{name='python'} Person{name='java'} Person{name='scala'} Person{name='python'} Process finished with exit code 0 */
示例7:
public class StreamDemo { public static void main(String[] args) { String str ="11,22,33,44,55"; //将str中的每一个数值都打印出来,同时算出最终的求和结果 System.out.println(Stream.of(str.split(",")).peek(System.out::println).mapToInt(Integer::valueOf).sum()); } }
打印结果为:
/* 11 22 33 44 55 165 Process finished with exit code 0 */
示例8:
public class StreamDemo { public static void main(String[] args) { List<Integer> list = Arrays.asList(1,2, 3,4, 5, 6); System.out.println(list.stream().allMatch(x -> x>=0)); }
打印结果为:
/* true Process finished with exit code 0 */
六、自定义注解
1、什么是注解?
▪ Annontation是Java5开始引入的新特征,中文名称叫注解。
▪ 它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。
▪ 为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具戒框架使用。
▪ Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
▪ Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。
▪ 注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在java.lang.annotation 包中。
2、注解的作用
▪ 生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等 /** + 回车就会看到
▪ 跟踪代码依赖性,实现替代配置文件功能。
▪ 在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
3、注解的原理
反射
4、内置注解
▪ @Override:定义在java.lang.Override中,此注释叧适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
▪ @Deprecated:定义在java.lang.Deprecated中,此注释可以修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常
是因为它很危险或者存在更好的选择
▪ @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编写编译时的警告信息 比如定义一个变量没用到会是灰色,加@SuppressWarnings("all") 就会是正常的黑色
5、元注解
▪ 元注解的做用是负责注解其他注解,java中定义了四个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
▪ 这些类型和它们所支持的类在java.lang.annotation包中
– @Target:用来描述注解的使用范围(注解可以用在什么地方)
– @Retention:表示需要在什么级别保存该注释信息,描述注解的生命周期
▪ Source < Class < Runtime
– @Document:说明该注解将被包含在javadoc中
– @Inherited:说明子类可以继承父类中的该注解
6、自定义注解
▪ 使用@interfac自定义注解时,自动继承了
java.lang.annotation.Annotation接口
▪ 使用规则:
– @interface用来声明一个注解,格式:public @interface 注解名{}
– 其中的每一个方法实际上是声明了一个配置参数
– 方法的名称就是参数的名称
– 返回值类型就是参数的类型(返回值叧能是基本类型,Class,String,enum)
– 可以用default来声明参数的默认值
– 如果叧有一个参数成员,一般参数名为value
– 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
//target用来声明当前自定义的注解适合适用于什么地方,类,方法,变量,包。。。。 @Target({ElementType.METHOD,ElementType.TYPE}) //retention用来表示当前注解适用于什么环境,是源码级别还是类级别还是运行时环境,一般都是运行时环境 @Retention(RetentionPolicy.CLASS) //表示该注解是否是显示在javadoc中 @Documented //表示当前注解是否能够被继承 @Inherited @interface MyAnnotation{ //定义的方式看起来像是方法,但是实际上使用在使用注解的时候填写的参数的名称,默认的名称是value //自定义注解中填写的所有方法都需要在使用注解的时候,添加值,很麻烦,因此包含默认值 String name() default "zhangsan"; int age() default 12; int id() default 1; String[] likes() default {"a","b","c"}; }
//@MyAnnotation(name="hehe",age=12,id=3,likes = {"book","lol","movie"}) @MyAnnotation public class MetaAnnotation { public void test(){ } }