java虚拟机9

Java中的泛型

泛型是什么?

  • 泛型,即“参数化类型”,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
  • 引入一个类型变量 T(其他大写字母都可以,不过常用的就是 T,E,K,V 等等),并且用<>括起来,并放在类名的后面。泛型类是允许有多个类型变量的。
  • 按照约定,类型参数名称命名为单个大写字母,以便可以在使用普通类或接口名称时能够容易地区分类型参数。以下是常用的类型参数名称列表:
    • E - 元素,主要由 Java 集合(Collections)框架使用。
    • K - 键,主要用于表示映射中的键的参数类型。
    • V - 值,主要用于表示映射中的值的参数类型。
    • N - 数字,主要用于表示数字。
    • T - 类型,主要用于表示第一类通用型参数。
    • S - 类型,主要用于表示第二类通用类型参数。
    • U - 类型,主要用于表示第三类通用类型参数。
    • V - 类型,主要用于表示第四个通用类型参数。

泛型类和泛型接口、泛型方法

泛型类
  • package ex9.gengric;
    
    /**
     * 泛型类
     * 引入一个类型变量T(其他大写字母都可以,不过常用的就是T,E,K,V等等)
     */
    public class NormalGeneric<T> {
          
          
        private T data;
    
        public NormalGeneric() {
          
          
        }
    
        public NormalGeneric(T data) {
          
          
            this();
            this.data = data;
        }
    
        public T getData() {
          
          
            return data;
        }
    
        public void setData(T data) {
          
          
            this.data = data;
        }
    
        public static void main(String[] args) {
          
          
            NormalGeneric<String> normalGeneric = new NormalGeneric<>();
            normalGeneric.setData("King");
            System.out.println(normalGeneric.getData());
        }
    }
    
    
  • 在类开头的地方,可以通过单个字母来表示泛型,但是真正在在实例化这个类时就必须指明具体的类

泛型接口
  • package ex9.gengric;
    
    /**
     *泛型接口
     * 引入一个类型变量T(其他大写字母都可以,不过常用的就是T,E,K,V等等)
     */
    public interface Generator<T> {
          
          
        public T next();
    }
    
    
  • 泛型接口实现类1

    • package ex9.gengric;
      
      /**
       * 实现泛型类,方式1
       * 引入一个类型变量T(其他大写字母都可以,不过常用的就是T,E,K,V等等)
       */
      public class ImplGenerator<T> implements Generator<T> {
              
              
      
          private T data;
      
          public ImplGenerator(T data) {
              
              
              this.data = data;
          }
      
          @Override
          public T next() {
              
              
              return data;
          }
      
          public static void main(String[] args) {
              
              
              ImplGenerator<String> implGenerator = new ImplGenerator<>("King");
              System.out.println(implGenerator.next());
          }
      }
      
      
  • 泛型接口实现类2

    • package ex9.gengric;
      
      /**
       *  实现泛型类,方式2
       */
      public class ImplGenerator2 implements Generator<String> {
              
              
          @Override
          public String next() {
              
              
              return "King";
          }
      
          public static void main(String[] args) {
              
              
              ImplGenerator2 implGenerator2 = new ImplGenerator2();
              System.out.println(implGenerator2.next());
      
          }
      }
      
      
    • 因为已经在实现类中指明了类型,所以就跟普通类的实例化一样了

泛型方法
  • package ex9.gengric;
    
    /**
     * 为什么需要泛型
     */
    public class NeedGeneric {
          
          
    
        public int addInt(int x,int y){
          
          
            return x+y;
        }
    
        public float addFloat(float x,float y){
          
          
            return x+y;
        }
        public Double addDouble(Double x,Double y){
          
          
            return x+y;
        }
    
        public static void main(String[] args) {
          
          
            //不使用泛型
            NeedGeneric needGeneric = new NeedGeneric();
            System.out.println(needGeneric.addInt(1,2));
            System.out.println(needGeneric.addFloat(1.2f,2.4f));
    
            //使用泛型
            System.out.println(needGeneric.add(1,2));
            System.out.println(needGeneric.add(1.2d,2.4d));
    
        }
    
        //泛型方法
        public <T extends Number> double add(T x,T y){
          
          
            return x.doubleValue()+y.doubleValue();
        }
    }
    
    
  • 指定返回类型是Number的子类

为什么我们需要泛型

实例

  • package ex9.gengric;
    
    /**
     * 为什么需要泛型
     */
    public class NeedGeneric {
          
          
    
        public int addInt(int x,int y){
          
          
            return x+y;
        }
    
        public float addFloat(float x,float y){
          
          
            return x+y;
        }
        public Double addDouble(Double x,Double y){
          
          
            return x+y;
        }
    
        public static void main(String[] args) {
          
          
            //不使用泛型
            NeedGeneric needGeneric = new NeedGeneric();
            System.out.println(needGeneric.addInt(1,2));
            System.out.println(needGeneric.addFloat(1.2f,2.4f));
    
            //使用泛型
            System.out.println(needGeneric.add(1,2));
            System.out.println(needGeneric.add(1.2d,2.4d));
    
        }
    
        //泛型方法
        public <T extends Number> double add(T x,T y){
          
          
            return x.doubleValue()+y.doubleValue();
        }
    }
    
    
  • 所有的入参和出参本质上都是Number类,所以这样声明泛型

  • 好处?

    • 1.节约实现方法的数量
    • 2.泛型中间,它的数据类型是在使用的时候去指定,就不需要强制类型转换

虚拟机是如何实现泛型的?

实例

  • package ex9.gengric;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 泛型擦除
     */
    public class Theory {
          
          
        public static void main(String[] args) {
          
          
            Map<String,String> map = new HashMap<>();
            map.put("King","18");
            System.out.println(map.get("King"));
        }
    }
    
    
  • 使用jd-gui.exe打开class文件

    • package ex9.gengric;
      
      import java.io.PrintStream;
      import java.util.HashMap;
      import java.util.Map;
      
      public class Theory
      {
              
              
        public static void main(String[] args)
        {
              
              
          Map map = new HashMap();
          map.put("King", "18");
          System.out.println((String)map.get("King"));
        }
      }
      
    • JVM把泛型擦掉了,只有java代码中有这种单个字母的写法,并且JVM加入了强转代码

  • 使用javap -v查看字节码

    • public static void main(java.lang.String[]);
         descriptor: ([Ljava/lang/String;)V
         flags: ACC_PUBLIC, ACC_STATIC
         Code:
           stack=3, locals=2, args_size=1
              0: new           #2                  // class java/util/HashMap
              3: dup
              4: invokespecial #3                  // Method java/util/HashMap."<init>":()V
              7: astore_1
              8: aload_1
              9: ldc           #4                  // String King
             11: ldc           #5                  // String 18
             13: invokeinterface #6,  3            // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
             18: pop
             19: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
             22: aload_1
             23: ldc           #4                  // String King
             25: invokeinterface #8,  2            // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
             30: checkcast     #9                  // class java/lang/String
             33: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             36: return
           LineNumberTable:
             line 11: 0
             line 12: 8
             line 13: 19
             line 14: 36
           LocalVariableTable:
             Start  Length  Slot  Name   Signature
                 0      37     0  args   [Ljava/lang/String;
                 8      29     1   map   Ljava/util/Map;
           LocalVariableTypeTable:
             Start  Length  Slot  Name   Signature
                 8      29     1   map   Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;
      

泛型擦除

并不是真正的擦除
  • package ex9.gengric;
    
    import java.util.List;
    
    /**
     * 泛型的注意事项
     * JDK的编译器是可以通过(方法的特征: 返回类型+ 方法名+ param)
     */
    public class Conflict {
          
          
    //    public static String method(List<String> stringList){
          
          
    //        System.out.println("List");
    //        return "OK";
    //    }
    
        public static Integer method(List<Integer> integerList){
          
          
            System.out.println("List");
            return 0;
        }
    }
    
    
  • 上面注释代码中的入参实际是不同的泛型,也就是不同的参数,但是在idea中无法编译通过,但是在jdk中是可以编译通过的,两者的返回类型实际是不同的

  • jdk1.5->1.6->1.7

    • 为了在jdk所有的版本泛型一直能用,保留了泛型信息,并没有完全擦除

    • public static java.lang.Integer method(java.util.List<java.lang.Integer>);
          descriptor: (Ljava/util/List;)Ljava/lang/Integer;
          flags: ACC_PUBLIC, ACC_STATIC
          Code:
            stack=2, locals=1, args_size=1
               0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
               3: ldc           #3                  // String List
               5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
               8: iconst_0
               9: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
              12: areturn
            LineNumberTable:
              line 16: 0
              line 17: 8
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0      13     0 integerList   Ljava/util/List;
            LocalVariableTypeTable:
              Start  Length  Slot  Name   Signature
                  0      13     0 integerList   Ljava/util/List<Ljava/lang/Integer;>;
          Signature: #22                          // (Ljava/util/List<Ljava/lang/Integer;>;)Ljava/lang/Integer;
      
    • 在上面method方法的局部变量表里面,其中签名Signature会记录是integer

  • 在反射调用中,具体的数据类型就是从泛型信息中去获取,所以JVM这种实现也叫弱记忆

弱记忆(版本的兼容性)

Java中的Stream

什么是Stream?

实例

  • package ex9.stream;
    
    import java.util.*;
    
    /**
     * @author King老师
     * Stream使用入门
     */
    public class StreamDemo {
          
          
        public static void main(String[] args) {
          
          
    
            List<String> names = Arrays.asList("张三", "李四", "王老五", "李三", "刘老四", "王小二", "张四", "张五六七");
            //找出姓张的最长名字的长度
    //        List ll = new ArrayList();
    //        for(String name:names){
          
          
    //            if(name.startsWith("张")){
          
          
    //                ll.add(name.length());
    //            }
    //        }
    //        int maxLenZ = (int) Collections.max(ll);
       //     System.out.println(maxLenZ);
            //使用stream一行代码解决了。
            int maxLenZ = names.parallelStream()
                    .filter(name -> name.startsWith("张"))
                    .mapToInt(String::length)
                    .max()
                    .getAsInt();
            System.out.println(maxLenZ);
        }
    
    }
    
    
  • 从一个list中找出姓张的最长名字的长度

为什么要使用Stream?

  • 精简代码

Stream使用入门

  • 见实例

Stream操作分类

终结操作

  • 中间操作可以有多个,但是终结操作只能写一个

  • 非短路操作

    • collect()
    • max(),最大值
    • min()
    • count()
    • forEach()
    • forEachOrdered()
    • toArray()
    • reduce()
  • 短路操作,在进行匹配时,只要有匹配结果了就可以出来最终结果,比如只要有任何一项匹配就返回结果

    • anyMatch()
    • allMatch()
    • findFirst()
    • findAny()
    • noneMatch()

中间操作

  • 中间操作是懒操作,最终真正在终结操作处一起执行

  • 无状态,当前元素执行无状态操作与它的前一个元素无关,而又状态就是相关

    • map()
    • filter(),过滤操作
    • unordered()
    • peek()
  • 有状态

    • distinct(),去掉重复的
    • sorted(),排序需要比较所有的元素
    • limit()
    • skip()

实例

  • package ex9.stream;
    
    import ex9.stream.test.Student;
    
    import java.util.*;
    import java.util.stream.Collectors;
    /**
     * @author King老师
     * Stream使用
     */
    public class StuWithStream {
          
          
        public static void main(String[] args) {
          
          
            List<Student> studentList =Datainit();
            groupBy(studentList);
           // filter(studentList);
            total(studentList);
            MaxAndMin(studentList);
    
        }
        public static List<Student> Datainit(){
          
          
            List<Student> students = Arrays.asList(
                    new Student("小明", 168, "男"),
                    new Student("大明", 182, "男"),
                    new Student("小白", 174, "男"),
                    new Student("小黑", 186, "男"),
                    new Student("小红", 156, "女"),
                    new Student("小黄", 158, "女"),
                    new Student("小青", 165, "女"),
                    new Student("小紫", 172, "女"));
            return students;
        }
        //Stream实现分组
        public static  void groupBy(List<Student> studentsList){
          
          
            Map<String, List<Student>> groupBy = studentsList
                    .stream()
                    .collect(Collectors.groupingBy(Student::getSex));
            System.out.println("分组后:"+groupBy);
        }
    
        //Stream实现过滤
        public static  void filter(List<Student> studentsList){
          
          
            List<Student> filter = studentsList
                    .stream()
                    .filter(student->student.getHeight()>180)
                    .collect(Collectors.toList());
            System.out.println("过滤后:"+filter);
        }
    
        //Stream实现求和
        public static  void total(List<Student> studentsList){
          
          
            int totalHeight = studentsList
                    .stream()
                    .mapToInt(Student::getHeight)
                    .sum();
            System.out.println(totalHeight);
        }
        //Stream找最大和最小
        public static  void MaxAndMin(List<Student> studentsList){
          
          
            int maxHeight = studentsList
                    .stream()
                    .mapToInt(Student::getHeight)
                    .max()
                    .getAsInt();
            System.out.println("max:"+maxHeight);
            int minHeight = studentsList
                    .stream()
                    .mapToInt(Student::getHeight)
                    .min()
                    .getAsInt();
            System.out.println("min:"+minHeight);
        }
    
    
    
    }
    
    

Stream的底层实现

Stream源码实现

实例

  • package ex9.stream;
    
    import java.util.*;
    
    /**
     * @author King老师
     * Stream使用入门
     */
    public class StreamDemo {
          
          
        public static void main(String[] args) {
          
          
    
            List<String> names = Arrays.asList("张三", "李四", "王老五", "李三", "刘老四", "王小二", "张四", "张五六七");
            //找出姓张的最长名字的长度
    //        List ll = new ArrayList();
    //        for(String name:names){
          
          
    //            if(name.startsWith("张")){
          
          
    //                ll.add(name.length());
    //            }
    //        }
    //        int maxLenZ = (int) Collections.max(ll);
       //     System.out.println(maxLenZ);
            //使用stream一行代码解决了。
            int maxLenZ = names.stream()
                    .filter(name -> name.startsWith("张"))
                    .mapToInt(String::length)
                    .max()
                    .getAsInt();
            System.out.println(maxLenZ);
        }
    
    }
    
    
  • .stream()

    • default Stream<E> stream() {
              
              
          return StreamSupport.stream(spliterator(), false);
      }
      
    • public static <T> Stream<T> stream(Spliterator<T> spliterator, boolean parallel) {
              
              
          Objects.requireNonNull(spliterator);
          return new ReferencePipeline.Head<>(spliterator,
                                              StreamOpFlag.fromCharacteristics(spliterator),
                                              parallel);
      }
      
    • ReferencePipeline.Head:相当于是管道的头

  • .filter()

    • public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
              
              
          Objects.requireNonNull(predicate);
          return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
                                               StreamOpFlag.NOT_SIZED) {
              
              
              @Override
              Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
              
              
                  return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
              
              
                      @Override
                      public void begin(long size) {
              
              
                          downstream.begin(-1);
                      }
      
                      @Override
                      public void accept(P_OUT u) {
              
              
                          if (predicate.test(u))
                              downstream.accept(u);
                      }
                  };
              }
          };
      }
      
    • 返回一个StatelessOp

  • .max()

    • public final OptionalInt max() {
              
              
          return reduce(Math::max);
      }
      
    • public final OptionalInt reduce(IntBinaryOperator op) {
              
              
          return evaluate(ReduceOps.makeInt(op));
      }
      
    • final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
              
              
          assert getOutputShape() == terminalOp.inputShape();
          if (linkedOrConsumed)
              throw new IllegalStateException(MSG_STREAM_LINKED);
          linkedOrConsumed = true;
      
          return isParallel()
                 ? terminalOp.evaluateParallel(this, sourceSpliterator(terminalOp.getOpFlags()))
                 : terminalOp.evaluateSequential(this, sourceSpliterator(terminalOp.getOpFlags()));
      }
      

      parallelStream是并发的,stream是串行的

    • public <P_IN> R evaluateSequential(PipelineHelper<T> helper,
                                         Spliterator<P_IN> spliterator) {
              
              
          return helper.wrapAndCopyInto(makeSink(), spliterator).get();
      }
      
    • final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
              
              
          // 
          copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
          return sink;
      }
      
    • final <P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
              
              
          Objects.requireNonNull(sink);
      
          // 有两个操作
          for ( @SuppressWarnings("rawtypes") AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
              
              
              sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
          }
          return (Sink<P_IN>) sink;
      }
      

      生成sink链表

    • final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
              
              
          Objects.requireNonNull(wrappedSink);
      
          if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
              
              
              wrappedSink.begin(spliterator.getExactSizeIfKnown());
              spliterator.forEachRemaining(wrappedSink);
              wrappedSink.end();
          }
          else {
              
              
              copyIntoWithCancel(wrappedSink, spliterator);
          }
      }
      

      真正开始执行之前的中间操作,wrappedSink.begin,wrappedSink.end

Stream 操作叠加

管道模式

  • Sink1->Sink2->Sink3->ReducingSink,一级一级的管道处理,并且是叠加的

Stream串行处理底层分析

  • stream

Stream并行处理底层分析

  • parallelStream

Stream的性能

性能测试对比

常规的迭代

  • 数据量较小,100个对象

  • package ex9.stream.test;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    
    /**
     * @author King老师
     * 性能测试对比
     */
    public class App 
    {
          
          
        public static void main( String[] args )
        {
          
          
    		//100个对象
    		int[] arr = new int[100];
    		//parallelStream的地方都是使用同一个Fork-Join线程池,而线程池线程数仅为cpu的核心数
    		//可以通过一下配置修改这个值
    		System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","240");
    		Random r = new Random();
    		for(int i=0; i<arr.length; i++){
          
          
    			arr[i] = r.nextInt();
    		}
    
            // 找出身高大于160的,没有使用stream
    		IteratorTest.IteratorForIntTest(arr);//1
            // 使用stream,并且groupby
    		SerialStreamTest.SerialStreamForIntTest(arr);//2
            // 使用并行的stream
    		ParallelStreamTest.ParallelStreamForIntTest(arr);//3
        	
        	
        	
        	
    //		//一个亿
    //    	int[] arr = new int[100000000];
    //		//parallelStream的地方都是使用同一个Fork-Join线程池,而线程池线程数仅为cpu的核心数
    //		//可以通过一下配置修改这个值
    //		System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","240");
    //    	Random r = new Random();
    //		for(int i=0; i<arr.length; i++){
          
          
    //			arr[i] = r.nextInt();
    //		}
    //
    //    	IteratorTest.IteratorForIntTest(arr);//1
    //    	SerialStreamTest.SerialStreamForIntTest(arr);//2
    //    	ParallelStreamTest.ParallelStreamForIntTest(arr);//3
    		
    
        }
    }
    
    
  • 常规最快,串行最慢,并行第二

  • 大数据量,1亿

  • package ex9.stream.test;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    
    /**
     * @author King老师
     * 性能测试对比
     */
    public class App 
    {
          
          
        public static void main( String[] args )
        {
          
          
    //		//一个亿
    //		int[] arr = new int[100];
    //		//parallelStream的地方都是使用同一个Fork-Join线程池,而线程池线程数仅为cpu的核心数
    //		//可以通过一下配置修改这个值
    //		System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","240");
    //		Random r = new Random();
    //		for(int i=0; i<arr.length; i++){
          
          
    //			arr[i] = r.nextInt();
    //		}
    //
    //		IteratorTest.IteratorForIntTest(arr);//1
    //		SerialStreamTest.SerialStreamForIntTest(arr);//2
    //		ParallelStreamTest.ParallelStreamForIntTest(arr);//3
    
    
    
    
    		//一个亿
        	int[] arr = new int[100000000];
    		//parallelStream的地方都是使用同一个Fork-Join线程池,而线程池线程数仅为cpu的核心数
    		//可以通过一下配置修改这个值
    		System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","240");
        	Random r = new Random();
    		for(int i=0; i<arr.length; i++){
          
          
    			arr[i] = r.nextInt();
    		}
    
        	IteratorTest.IteratorForIntTest(arr);//1
        	SerialStreamTest.SerialStreamForIntTest(arr);//2
        	ParallelStreamTest.ParallelStreamForIntTest(arr);//3
    
    
        }
    }
    
    
  • stream并行>stream串行>常规的迭代

    • 需要机器是多核

Stream 串行迭代

Stream 并行迭代

如何合理使用 Stream?

  • 在大数据量,同时要结合计算机CPU情况,并行>串行>普通迭代

猜你喜欢

转载自blog.csdn.net/Markland_l/article/details/115051245