Java-08 泛型 、ArrayList

泛型 (Generics)

  • 從java 5開始,增加了泛型技術(shù)

什么是泛型?

  • 將類型變?yōu)閰?shù),提高代碼的復(fù)用率
// T:類型參數(shù)
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);

泛型類型的類型參數(shù)只能用于實(shí)例方法中,不能用于靜態(tài)方法。因?yàn)榉盒皖愋偷念愋蛥?shù)是跟實(shí)例相關(guān)聯(lián)的

public class Student<T> {
  private T score;
  public Student(T score ) {
    this.score = score;
  }
  // 報(bào)錯(cuò)
  public static void print(T e) {
    
  }
}

但是可以使用泛型方法

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

  }

可以通過extends對類型參數(shù)進(jìn)行限制,比如<T extends A>

extends后面可以跟上類名,接口名,代表T必須是A類型,或者繼承/實(shí)現(xiàn)了A。 跟dart類似

public class Student<T extends Number> {

}

可以同時(shí)添加多個(gè)限制,比如<T extends A & B & C>,代表T必須同時(shí)滿足A、B、C

不過有一個(gè)需要注意的是,類名需要放在接口的前面,且最多只能有一個(gè)類名(因?yàn)閖ava不支持多類型)

原始類型(Raw Type)

什么是原始類型?

  • 沒有傳遞具體的類型給泛型的類型參數(shù)

通配符

在泛型中,?被稱為通配符。通常用作變量類型,返回值類型的類型參數(shù)。不能用作泛型方法調(diào)用、泛型類型實(shí)例化、泛型類型定義的類型參數(shù)

通配符 - 上界

可以通過extends設(shè)置類型參數(shù)的上界

// 類型參數(shù)必須是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;
    // 報(bào)錯(cuò) 因?yàn)橄拗屏朔盒皖愋捅仨殲镹umber或其子類型
    box4 = box3;
    
    showBox(box1);
    showBox(box2);
    // 報(bào)錯(cuò)
    showBox(box3);
  }

  // 1. 使用泛型方法
  public static <T extends Number> void showBox(Box<T> box) {

  }
  // 2. 使用通配符
  // 類型參數(shù)必須是Number類型或者其子類型
  static void showBox(Box<? extends Number> box) {

  }
}

通配符- 下界

可以通過super設(shè)置類型參數(shù)的下屆

  // 類型參數(shù)必須是Integer類型或者Integer的父類型
  void testLower(Box<? super Integer> box) {

  }

通配符 - 無限制

  // 無限制 類型參數(shù)是什么類型都可以
  void test(Box<?>box) {

  }

ArrayList的常用方法

    ArrayList list = new ArrayList();
    list.add(11);
    list.add(false);
    list.add(null);
    // 添加元素到指定的下標(biāo)
    list.add(0, "Jack");

    System.out.println(list.size());

類似于OC中的可變數(shù)組

遍歷

    ArrayList<Integer> list = new ArrayList<>();
    list.add(11);
    list.add(22);
    list.add(33);
    // 遍歷
    // 經(jīng)典
    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 本質(zhì)也是使用迭代器 跟上面方法本質(zhì)上是一樣的
    for (Integer integer : list) {
      System.out.println(integer);
    }

    list.forEach(new Consumer<Integer>() {
      @Override
      public void accept(Integer t) {
        System.out.println(t);
      }
    });
    // lambda表達(dá)式
    list.forEach((i) -> {
      System.out.println(i);
    });

    list.forEach((i) -> System.out.println(i));

    // 方法引用簡化
    list.forEach(System.out::println);

for-each 格式

    // for-each 格式
    for (元素類型 變量名 : 數(shù)組\Iterable) {

    }

實(shí)現(xiàn)了Iterable接口的對象,都可以使用for-each格式遍歷元素, 比如ListSet

Iterabel在使用for-each格式遍歷元素時(shí),本質(zhì)上使用了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() {
    // 返回一個(gè)迭代器
    return new ClassRoomIterator();
  }
  // 自定義一個(gè)迭代器
  private class ClassRoomIterator implements Iterator<String> {
    private int cursor;
    @Override
    public boolean hasNext() {
      // 是否有下一個(gè)元素 取決于游標(biāo)指向的位置是否超過數(shù)組邊界
      return cursor < students.length;
    }
    @Override
    public String next() {
      // 取出下一個(gè)元素
      // 1. 取出當(dāng)前游標(biāo)指向的元素
      // 2. 將游標(biāo)指向下一個(gè)位置
      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());
    }
  }
}
循環(huán)刪除元素

循環(huán)刪除元素時(shí) 可以使用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的擴(kuò)容

ArrayList每次擴(kuò)容都是上一次容量的1.5倍

// 計(jì)算新的容量
private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 原來的容量右移一位 再加上原來的容量
        // 加法與位移運(yùn)算的效率是高于乘法/除法的
        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);
    }

當(dāng)添加的元素?cái)?shù)量很多時(shí),可能會(huì)多次擴(kuò)容,為了優(yōu)化性能,可以考慮一次擴(kuò)容(前提是知道擴(kuò)容的大?。?/p>

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

當(dāng)容量很大,但實(shí)際存儲的元素很少時(shí),可以考慮適當(dāng)?shù)目s容

      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, 但最后實(shí)際上只存放了10個(gè)元素,可以考慮縮容

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

trimToSize 把元素拷貝到更小的數(shù)組中(長度為size)

LinkedList

LinkedList是一個(gè)雙向鏈表,跟ArrayList一樣都實(shí)現(xiàn)了List接口。API跟ArrayList類似

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

LinkedList跟ArrayList的區(qū)別

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容