一、集合框架概述

集合概述

集合、数组都是对多个数据进行存储操作的结构,简称Java容器。

数组在存储多个数据方面的特点

一方面

  • 一旦初始化以后,其长度就确定了。
  • 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
    • 如:String[] arr; int[] arr1; Object[] arr2;

另一方面

  • 一旦初始化以后,其长度就不可修改。
  • 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
  • 没有现成的属性或方法获取数组中实际元素的个数。
  • 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

    集合框架

  • Collection接口:单列集合,用来存储一个一个的对象

    • List接口:存储有序的、可重复的数据。(“动态”数组)
      • ArrayList、LinkedList、Vector
    • Set接口:存储无序的、不可重复的数据。(类似数学中的集合)
      • HashSet、LinkedHashSet、TreeSet
  • Map接口:双列集合,用来存储一对一对的数据(key-value)

    • HashMap、LinkedHashMap、TreeMap、HashTable、Properties

      二、Collection接口常用方法

      常用方法

      ```java @Test // Collection接口中声明的方法测试 public void test1() { Collection collection = new ArrayList();

      // 1.add():添加元素 collection.add(“AA”); collection.add(“BB”); collection.add(123); collection.add(new Date());

      // 2.size():获取添加的元素的个数 System.out.println(“size of collection —> “ + collection.size());

      // 3.addAll():将collection集合中的元素添加到当前的集合中 Collection collection_01 = new ArrayList(); collection_01.add(456); collection_01.add(“CC”); collection_01.addAll(collection);

      // 4.clear():清空集合元素 collection.clear();

      // 5.isEmpty():判断当前集合是否为空 System.out.println(“is Empty ? —> “ + collection.isEmpty());

      // 6.contains():是否包含,比较对象时使用equals方法 collection.add(new String(“Tom”)); collection.add(new Person(“Jerry”,20)); boolean contains = collection.contains(123); System.out.println(“contains 123 ? —> “ + contains); contains = collection.contains(new Person(“Jerry”,20)); System.out.println(“contains Person ? —> “ + contains);

      // 7.containsAll(Collection c):判断形参c中的所有元素是否都存在于当前集合中 Collection c = Arrays.asList(123, 456); System.out.println(“contaionsAll ? —> “ + collection.containsAll(c));

      // 8.remove(Object obj): System.out.println(“before remove: “ + collection); collection.remove(“Tom”); System.out.println(“after remove: “ + collection);

      // 9.removeAll(Collection c):从当前集合中移除c中所有的元素 collection.add(123); collection.add(456); collection.add(“Tom”); System.out.println(“before removeAll: “ + collection); collection.removeAll(Arrays.asList(456,789)); System.out.println(“after removeAll: “ + collection);

      // 10.retainAll(Collection c):保留当前集合和集合c的交集, collection.retainAll(collection_01); System.out.println(“after retainAll: “ + collection);

      // 11.equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同。 System.out.println(“equals? —> “ + collection.equals(collection_01));

      // 12.hashCode():返回当前对象的哈希值 System.out.println(“hashCode: “ + collection.hashCode());

      // 13.toArray():集合—>数组 Object[] array = collection.toArray(); System.out.println(“Array:”); for (int i = 0; i < array.length; i++) { System.out.println(array[i]); }

      // else: asList() 数组—>集合:调用Arrays类的静态方法 List list = Arrays.asList(new String[]{“AA”,”BB”,”CC”}); System.out.println(list);

      List list1 = Arrays.asList(new int[]{123, 456}); System.out.println(list1.size());

      List list2 = Arrays.asList(new Integer[]{123, 456}); System.out.println(list2.size());

}

  1. 输出结果:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21650681/1638196166815-af971872-e095-4d11-ae73-bd019ee3a88b.png#clientId=u7a1df733-dccd-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=290&id=u1048e36a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=580&originWidth=1027&originalType=binary&ratio=1&rotation=0&showTitle=false&size=45066&status=done&style=none&taskId=u478cfc74-2b39-41a5-a920-6a12250a785&title=&width=513.5)
  2. <a name="QgiaS"></a>
  3. ## Iterator接口与foreach循环
  4. ```java
  5. @Test // 集合元素遍历
  6. public void IteratorTest() {
  7. Collection collection = new ArrayList();
  8. collection.add(123);
  9. collection.add(456);
  10. collection.add("Tom");
  11. collection.add(new Person("Jerry",17));
  12. Iterator iterator = collection.iterator();
  13. while (iterator.hasNext()) {
  14. System.out.println(iterator.next());
  15. }
  16. // jdk5.0新增foreach循环,用于遍历集合、数组
  17. System.out.println("===========foreach============");
  18. for (Object o : collection) {
  19. System.out.println(o);
  20. }
  21. for (int i : new int[]{1,2,3}) {
  22. System.out.println(i);
  23. }
  24. }
  25. @Test // 增强for循环与普通for循环
  26. public void forTest() {
  27. String[] arr = new String[]{"MM","MM","MM"};
  28. // 普通for循环
  29. for (int i = 0; i < arr.length; i++) {
  30. arr[i] = "GG";
  31. }
  32. // 增强for循环
  33. for (String s : arr) {
  34. s = "GG";
  35. }
  36. for (int i = 0; i < arr.length; i++) {
  37. System.out.println(arr[i]);
  38. }
  39. }

三、Collection子接口:List

1、存储特点

存储有序的、可重复的数据。(注意:所在的类要重写equals方法)

2、常用方法

  • 增:add(Object obj)
  • 删:remove(int index) / remove(Object obj)
  • 改:set(int index, Object ele)
  • 查:get(int index)
  • 插:add(int index, Object ele)
  • 长度:size()
  • 遍历

    • Iterator迭代器方式
    • 增强for循环
    • 普通for循环

      3、常用实现类

  • ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储

  • LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
  • Vector:作为List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData存储

    4、源码分析

    ArrayList源码分析:

    jdk1.7版本

    ```java // 存储元素的数组 private transient Object[] elementData;

// 空参构造器,默认容量为10 public ArrayList() { this(10); }

//根据给定容量创建数组 public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) throw new IllegalArgumentException(“Illegal Capacity: “+initialCapacity); this.elementData = new Object[initialCapacity]; }

// 添加元素 public boolean add(E e) { // 确认容量 ensureCapacityInternal(size + 1); elementData[size++] = e; return true; }

// 确认容量大小,不够则扩容 private void ensureCapacityInternal(int minCapacity) { // 修改次数 modCount++; // 如果容量不够,则扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); }

// 扩容 private void grow(int minCapacity) { // 获取旧的容量大小 int oldCapacity = elementData.length; // 新的容量为原来的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果1.5倍容量不够,则直接设置为传入容量大小 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 容量上限 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 生成新的数组 elementData = Arrays.copyOf(elementData, newCapacity); }

  1. <a name="s47Kh"></a>
  2. #### jdk1.8版本
  3. ```java
  4. // 默认初始化容量
  5. private static final int DEFAULT_CAPACITY = 10;
  6. //空元素数组
  7. private static final Object[] EMPTY_ELEMENTDATA = {};
  8. // 默认初始化为空数组
  9. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  10. // 非private简化内部类访问
  11. transient Object[] elementData;
  12. // 初始化为{}
  13. public ArrayList() {
  14. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  15. }
  16. public ArrayList(int initialCapacity) {
  17. if (initialCapacity > 0) {
  18. this.elementData = new Object[initialCapacity];
  19. } else if (initialCapacity == 0) {
  20. this.elementData = EMPTY_ELEMENTDATA;
  21. } else {
  22. throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
  23. }
  24. }
  25. private void ensureCapacityInternal(int minCapacity) {
  26. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  27. }
  28. // 添加元素时初始容量为10或更大
  29. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  30. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  31. return Math.max(DEFAULT_CAPACITY, minCapacity);
  32. }
  33. return minCapacity;
  34. }

小结

jdk7中ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的对象创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

LinkedList源码分析:

元素用双链表结构存储

  1. // 头结点
  2. transient Node<E> first;
  3. // 尾结点
  4. transient Node<E> last;
  5. public LinkedList() {}
  6. public LinkedList(Collection<? extends E> c) {
  7. this();
  8. addAll(c);
  9. }
  10. // 结点内部类
  11. private static class Node<E> {
  12. E item;
  13. Node<E> next;
  14. Node<E> prev;
  15. Node(Node<E> prev, E element, Node<E> next) {
  16. this.item = element;
  17. this.next = next;
  18. this.prev = prev;
  19. }
  20. }
  21. // 添加结点
  22. public boolean add(E e) {
  23. linkLast(e);
  24. return true;
  25. }
  26. void linkLast(E e) {
  27. final Node<E> l = last;
  28. final Node<E> newNode = new Node<>(l, e, null);
  29. last = newNode;
  30. if (l == null)
  31. first = newNode;
  32. else
  33. l.next = newNode;
  34. size++;
  35. modCount++;
  36. }

Vector源码分析:

与ArrayList类似,但Vector是线程安全的

  1. // 方法中都添加了synchronized关键字
  2. public synchronized boolean add(E e) {
  3. modCount++;
  4. ensureCapacityHelper(elementCount + 1);
  5. elementData[elementCount++] = e;
  6. return true;
  7. }
  8. // 扩容方面默认是原来的两倍
  9. private void grow(int minCapacity) {
  10. // overflow-conscious code
  11. int oldCapacity = elementData.length;
  12. int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
  13. capacityIncrement : oldCapacity);
  14. if (newCapacity - minCapacity < 0)
  15. newCapacity = minCapacity;
  16. if (newCapacity - MAX_ARRAY_SIZE > 0)
  17. newCapacity = hugeCapacity(minCapacity);
  18. elementData = Arrays.copyOf(elementData, newCapacity);
  19. }