美文网首页
Java-08 泛型 、ArrayList

Java-08 泛型 、ArrayList

作者: 哆啦_ | 来源:发表于2020-06-18 18:28 被阅读0次

    泛型 (Generics)

    • 从java 5开始,增加了泛型技术

    什么是泛型?

    • 将类型变为参数,提高代码的复用率
    // T:类型参数
    public class Student<T> {
      private T score;
      public Student(T score ) {
        this.score = score;
      }
    }
    
        Student<String> stu1 = new Student<String>("A");
        Student<Double> stu2 = new Student<Double>(88.8);
        
        // java 7之后 可以省略右边的类型
        Student<Integer> stu3 = new Student<>(44);
    

    泛型类型的类型参数只能用于实例方法中,不能用于静态方法。因为泛型类型的类型参数是跟实例相关联的

    public class Student<T> {
      private T score;
      public Student(T score ) {
        this.score = score;
      }
      // 报错
      public static void print(T e) {
        
      }
    }
    

    但是可以使用泛型方法

      public static <T1, T2>void print(T1 e, T2 s) {
    
      }
    

    可以通过extends对类型参数进行限制,比如<T extends A>

    extends后面可以跟上类名,接口名,代表T必须是A类型,或者继承/实现了A。 跟dart类似

    public class Student<T extends Number> {
    
    }
    

    可以同时添加多个限制,比如<T extends A & B & C>,代表T必须同时满足A、B、C

    不过有一个需要注意的是,类名需要放在接口的前面,且最多只能有一个类名(因为java不支持多类型)

    原始类型(Raw Type)

    什么是原始类型?

    • 没有传递具体的类型给泛型的类型参数

    通配符

    在泛型中,?被称为通配符。通常用作变量类型,返回值类型的类型参数。不能用作泛型方法调用、泛型类型实例化、泛型类型定义的类型参数

    通配符 - 上界

    可以通过extends设置类型参数的上界

    // 类型参数必须是Number类型或者其子类型
      static void showBox(Box<? extends Number> box) {
    
      }
    
    public class Main {
      public static void main(String[] args) {
        Box<Integer> box1 = new Box<>();
        Box<Double> box2 = new Box<>();
        Box<Object> box3 = new Box<>();
    
        Box<? extends Number> box4 = null;
        box4 = box1;
        box4 = box2;
        // 报错 因为限制了泛型类型必须为Number或其子类型
        box4 = box3;
        
        showBox(box1);
        showBox(box2);
        // 报错
        showBox(box3);
      }
    
      // 1. 使用泛型方法
      public static <T extends Number> void showBox(Box<T> box) {
    
      }
      // 2. 使用通配符
      // 类型参数必须是Number类型或者其子类型
      static void showBox(Box<? extends Number> box) {
    
      }
    }
    

    通配符- 下界

    可以通过super设置类型参数的下届

      // 类型参数必须是Integer类型或者Integer的父类型
      void testLower(Box<? super Integer> box) {
    
      }
    

    通配符 - 无限制

      // 无限制 类型参数是什么类型都可以
      void test(Box<?>box) {
    
      }
    

    ArrayList的常用方法

        ArrayList list = new ArrayList();
        list.add(11);
        list.add(false);
        list.add(null);
        // 添加元素到指定的下标
        list.add(0, "Jack");
    
        System.out.println(list.size());
    

    类似于OC中的可变数组

    遍历

        ArrayList<Integer> list = new ArrayList<>();
        list.add(11);
        list.add(22);
        list.add(33);
        // 遍历
        // 经典
        int size = list.size();
        for (int i = 0; i < size; i++) {
          System.out.println(list.get(i));
        }
        // 迭代器
        Iterator<Integer> it = list.iterator();
        while(it.hasNext()) {
          System.out.println(it.next());
        }
    
        // for-each 本质也是使用迭代器 跟上面方法本质上是一样的
        for (Integer integer : list) {
          System.out.println(integer);
        }
    
        list.forEach(new Consumer<Integer>() {
          @Override
          public void accept(Integer t) {
            System.out.println(t);
          }
        });
        // lambda表达式
        list.forEach((i) -> {
          System.out.println(i);
        });
    
        list.forEach((i) -> System.out.println(i));
    
        // 方法引用简化
        list.forEach(System.out::println);
    
    

    for-each 格式

        // for-each 格式
        for (元素类型 变量名 : 数组\Iterable) {
    
        }
    

    实现了Iterable接口的对象,都可以使用for-each格式遍历元素, 比如ListSet

    Iterabel在使用for-each格式遍历元素时,本质上使用了Iterator对象

    public class ClassRoom implements Iterable<String> {
      private String[] students;
      public ClassRoom(String... students) {
        this.students = students;
      }
    
      public String[] getStudents() {
        return students;
      }
    
      public void setStudents(String[] students) {
        this.students = students;
      }
      @Override
      public Iterator<String> iterator() {
        // 返回一个迭代器
        return new ClassRoomIterator();
      }
      // 自定义一个迭代器
      private class ClassRoomIterator implements Iterator<String> {
        private int cursor;
        @Override
        public boolean hasNext() {
          // 是否有下一个元素 取决于游标指向的位置是否超过数组边界
          return cursor < students.length;
        }
        @Override
        public String next() {
          // 取出下一个元素
          // 1. 取出当前游标指向的元素
          // 2. 将游标指向下一个位置
          return students[cursor++];
        }
      }
    }
    
    
    
    public class Main {
      public static void main(String[] args) {
        ClassRoom room = new ClassRoom("Jack","Bob");
        for (String name : room) {
          System.out.println(name);
        }
    
        Iterator<String> it = room.iterator();
        while(it.hasNext()) {
          System.out.println(it.next());
        }
      }
    }
    
    循环删除元素

    循环删除元素时 可以使用Iteratorremove方法

        ArrayList<Integer> list = new ArrayList<>();
        list.add(11);
        list.add(22);
        list.add(33);
        list.add(44);
    
        Iterator<Integer> it = list.iterator();
        while(it.hasNext()) {
          // 取出元素
          it.next();
          // 删除元素
          // 使用迭代器的remove方法 而不是list的remove方法
          it.remove();
        }
        System.out.println(list.toString());
    
    

    ArrayList的扩容

    ArrayList每次扩容都是上一次容量的1.5倍

    // 计算新的容量
    private int newCapacity(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            // 原来的容量右移一位 再加上原来的容量
            // 加法与位移运算的效率是高于乘法/除法的
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity <= 0) {
                if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                    return Math.max(DEFAULT_CAPACITY, minCapacity);
                if (minCapacity < 0) // overflow
                    throw new OutOfMemoryError();
                return minCapacity;
            }
            return (newCapacity - MAX_ARRAY_SIZE <= 0)
                ? newCapacity
                : hugeCapacity(minCapacity);
        }
    
    

    当添加的元素数量很多时,可能会多次扩容,为了优化性能,可以考虑一次扩容(前提是知道扩容的大小)

          ArrayList<Integer> list = new ArrayList<>();
          for (int i = 0; i < 5; i++) {
            list.add(i);
          }
          // 提前扩容
          list.ensureCapacity(list.size() + 10000);
          for (int i = 0; i < 10000; i++) {
            list.add(i);
          }
    
    

    当容量很大,但实际存储的元素很少时,可以考虑适当的缩容

          ArrayList<Integer> list = new ArrayList();
          for (int i = 0; i < 10000; i++) {
            list.add(i);
          }
          list.clear();
    
          for (int i = 0; i < 10; i++) {
            list.add(i);
          }
          // 缩容
          list.trimToSize();
    

    上面的list的容量为10000, 但最后实际上只存放了10个元素,可以考虑缩容

    public void trimToSize() {
            modCount++;
            if (size < elementData.length) {
                elementData = (size == 0)
                  ? EMPTY_ELEMENTDATA
                  : Arrays.copyOf(elementData, size);
            }
        }
    

    trimToSize 把元素拷贝到更小的数组中(长度为size)

    LinkedList

    LinkedList是一个双向链表,跟ArrayList一样都实现了List接口。API跟ArrayList类似

          List<Integer> list = new LinkedList<>();
          for (int i = 0; i < 5; i++) {
            list.add(i);
          }
    

    LinkedList跟ArrayList的区别

    相关文章

      网友评论

          本文标题:Java-08 泛型 、ArrayList

          本文链接:https://www.haomeiwen.com/subject/rtjuxktx.html