初级提升 - 容器

返回目录

容器

    • 容器(interface):开发和学习中需要时刻和数据打交道,一般通过“容器”容纳或管理容器,数组就是容器的一种。
      • 数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高,如果从效率和类型检查的角度来讲,数组是最好的;数组的劣势:不灵活,容量需要事先定义好,不能随着需求的变化而扩容,比如:用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?在写程序时是无法确定的,因此这种情况下就不能使用数组。
      • 基于数组并不能满足对于“管理和组织数据的需求”,所以需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是容器,也叫集合(Collection)。以下是容器的接口层次结构图:
    • 泛型(Generics):泛型是JDK1.5以后增加的,可以建立类型安全的集合。在使用了泛型的集合中,遍历时不必进行强制类型转换。JDK提供了支持泛型的编译器,将运行时的类型检查提前到了编译时执行,提高了代码可读性和安全性;泛型的本质就是“数据类型的参数化”,可以把“泛型”理解为数据类型的一个占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。
      package me.hzyis.test;
      public class Test {
        public static void main(String[] args) {
          // 这里的”String”就是实际传入的数据类型;
          MyCollection mc = new MyCollection(); //
          mc.set("黄泽雨", 001);
          String a = mc.get(1); //加了泛型,直接返回String类型,不用强制转换;
          System.out.println(a);
        }
      }
      // 可以在类的声明处增加泛型列表,如:<T,E,V>。
      class MyCollection{
        Object[] objs = new Object[5];
        public void set(E e ,int index) { //E表示泛型
          objs[index] = e;
        }
        public E get(int index) {
          return (E)objs[index];
        }
      }
    • Collection接口:Collection表示一组对象,它是集中、收集的意思;Collection接口的两个子接口是List、Set接口;由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有下面的方法。
      方法 说明
      boolean add(Object element) 增加元素到容器中
      boolean remove(Object element) 从容器中移除元素
      boolean contains(Object element) 检查容器中是否包含该元素
      int size() 容器中元素的数量
      boolean isEmpty() 容器是否为空
      void clear() 清空容器中的所有元素
      Iterator iterator() 获得迭代器,用于遍历所有元素
      boolean containsAll(Collection c) 本容器是否包含c容器中的所有元素
      boolean addAll(Collection c) 将容器c中所有元素增加到本容器
      boolean removeAll(Collection c) 移除本容器和容器c中相同的元素
      boolean retainAll(Collection c) 取本容器和容器c中相同的元素,移除非交集元素
      Object[] toArray() 转换成Object数组
      // list包含的方法,set相同
      package me.hzyis.test;
      import java.util.*;
      public class Test {
        public static void main(String[] args) {
          Collection c = new ArrayList<>();
          System.out.println("默认值:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          c.add("黄泽风");
          c.add("黄泽雨");
          System.out.println("添加后:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
      
          System.out.println("************************");
          System.out.println("是否包含<黄泽风>:"+c.contains("黄泽风"));
          System.out.println("是否包含<黄泽雷>:"+c.contains("黄泽雷"));
          
          System.out.println("************************");
          c.remove("黄泽风"); //移除c对象中“黄泽风”的引用地址,不是删除元素本身,移除后元素仍在内存中
          System.out.println("移除后:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          c.clear();
          System.out.println("清空后:"+c);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          Object[] toObject = c.toArray(); //转换为Object数组
          System.out.println("转换后:"+toObject);
          System.out.println("元素数量:"+c.size());
          System.out.println("是否为空:"+c.isEmpty());
          
          System.out.println("************************");
          Collection d = new ArrayList<>();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          System.out.println("d默认值:"+d);
          
          System.out.println("************************");
          d.addAll(c);
          System.out.println("d添加c后:"+d);
          
          System.out.println("************************");
          c.clear();
          d.clear();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          d.removeAll(c);
          System.out.println("d移除和c的相同元素后:"+d);
          
          System.out.println("************************");
          c.clear();
          d.clear();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          d.retainAll(c);
          System.out.println("d和c中相同元素:"+d);
          
          System.out.println("************************");
          c.clear();
          d.clear();
          c.add("黄泽风");
          c.add("黄泽雨");
          d.add("黄泽雨");
          d.add("黄老大");
          d.add("黄老二");
          System.out.println("d是否包含c中所有元素:"+d.containsAll(c));
          c.remove("黄泽风");
          System.out.println("d是否包含c中所有元素:"+d.containsAll(c));
        }
      }
    • List接口:有序:List中每个元素都有索引标记,可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素;可重复:List允许加入重复的元素,更确切地讲,List通常允许满足e1.equals(e2) 的元素重复加入容器;除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方法:
      方法 说明
      void add(int index, Object element) 在指定位置插入元素,前面的元素全部后移一位
      Object set(int index, Object element) 修改指定位置的元素
      Object get(int index) 返回指定位置的元素
      Object remove(int index) 删除指定位置的元素,后面的元素全部前移一位
      nt indexOf(Object o) 从前往后检索,根据提供的元素返回首个匹配元素的索引,如果没有该元素,返回-1
      int lastIndexOf(Object o) 从后往前检索,根据提供的元素返回首个匹配元素的索引,如果没有该元素,返回-1
      package me.hzyis.test;
      import java.util.*;
      public class Test {
        public static void main(String[] args) {
          List list = new ArrayList<>();
          list.add("test01");
          list.add("test02");
          list.add("test03");
          list.add("test04");
          list.add("test05");
          System.out.println(list);
          
          System.out.println("*****************************");
          list.add(2,"黄泽雨");
          System.out.println("索引2插入元素后:"+list);
          
          System.out.println("*****************************");
          list.remove(3);
          System.out.println("删除索引3元素后:"+list);
          
          System.out.println("*****************************");
          list.set(0, "黄泽风");
          System.out.println("设置索引0元素后:"+list);
          
          System.out.println("*****************************");
          System.out.println("索引1元素是:"+list.get(1));
          
          System.out.println("*****************************");
          list.add(3,"test05");
          System.out.println("当前排序:"+list);
          System.out.println("查找test05的顺序索引位置:"+list.indexOf("test05"));
          
          System.out.println("查找test05的倒序索引位置:"+list.lastIndexOf("test05"));
        }
      }
    • ArrayList:ArrayList底层是用数组实现的存储。 特点:查询效率高,增删效率低,线程不安全。我们一般使用它。数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,其方法就是创建一个更长的数组后,将原数组数据拷贝进新数组,新数据存放在老数组后面,从而实现扩容。
      // get方法
      public E get(int index) {
        rangeCheck(index); //检查索引是否正确
        return elementData(index); // 返回对应内容
      }
       
      // add方法
      public boolean add(E e) {
        ensureCapacityInternal(size + 1); //检查数组空位是否足够,不够则+1
        elementData[size++] = e;
        return true;
      }
       
      // 构造器,如果传入数组长度>0,
      public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
          // 大于0则new一个指定长度的数组
          this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
          // 如果为0则传入EMPTY_ELEMENTDATA的默认长度10
          // private static final int DEFAULT_CAPACITY = 10;
          // private static final Object[] EMPTY_ELEMENTDATA = {};
          this.elementData = EMPTY_ELEMENTDATA;
        } else {
          throw new IllegalArgumentException("Illegal Capacity: "+
            initialCapacity);
        }
      }
       
      // 扩容
      private void grow(int minCapacity) {
        int oldCapacity = elementData.length; //老容量=数组长度
        int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量=老容量+(老容量/2),假设老容量=10,则新容量=15
        if (newCapacity - minCapacity < 0)
          newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
          newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity); //把老数组元素拷贝到新数组后覆盖老数组,实现扩容
      }
    • LinkedList:LinkedList底层用双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全。双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中的任意一个节点开始,都可以很方便地找到所有节点。