五、集合(一)Collection

五、集合(一)Collection

1、数组存储数据有什么缺点?为什么需要用到集合?
  1. 数组初始化后,长度就确定了,不便于扩展
  2. 数组中提供的方法和属性较少,不便于进行添加、删除、插入等操作,且效率不高,同时无法直接获取到存储元素的个数(存储元素的个数和数组长度不是同一概念)
  3. 数组存储的数据是有序的、可重复的,存储数据的特点有点单一
2、集合的继承树

Collection继承树

3、关于Collection接口
  1. Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合
  2. 常用方法:
    1. add(T t) addAll(Collection coll) 添加元素(或集合)
    2. int size() 获取有效元素的个数
    3. void clear() 清空集合
    4. boolean isEmpty() 判断是否是空集合
    5. boolean contains(T t) 、boolean containsAll(Collection coll) 通过元素的equals() 方法来判断是否是同一个对象
    6. boolean remove(T t) 、boolean removeAll(Collection coll) 通过元素的equals() 方法来判断是否要删除那个元素
    7. boolean retainAll(Collection coll) 取两个集合的交集
    8. boolean equals(T t) 判断两个集合是否相等
    9. T toArray() 转换成为数组对象
    10. int hashCode() 获取集合对象的hash值
    11. iterator() 返回迭代器对象,用于集合的遍历
4、关于迭代器Iterator接口
  1. Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素

  2. Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了 Iterator接口的对象

  3. Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合

  4. 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前

  5. 主要方法:

    1. hasNext() 判断是否还有下一个元素

    2. T next() 将指针下移,将下移之后的集合上的元素返回

    3. default void remove() 从集合底层删除此迭代器的最后一个元素

      /**
       * 注意:
       * 1.如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove() 方法,再调用remove()
       * 都会报IllegalStateException,1.是因为指针还没下移,2.是因为已经删除了
       * 2.每次遍历操作都要执行获取新的Iterator对象,因为上次的Iterator的指针已经在集合的末尾了
       */
      @Test
          public void test1(){
              //创建一个Collection对象
              Collection<Integer> coll = new ArrayList<>();
              //添加元素
              coll.add(123);
              coll.add(456);
              coll.add(789);
              coll.add(110);
              //获取迭代器Iterator对象
              Iterator<Integer> iterator = coll.iterator();
              //利用迭代器 iterator进行遍历
              while (iterator.hasNext()){
                  System.out.println(iterator.next());
              }
          }
      
5、关于foreach循环
/**
     * 关于foreach
     * 内部使用的依然是迭代器
     * 判断输出结果
     */
    @Test
    public void test2(){
        String[] arr = new String[]{"MM","MM","MM"};
        for (String s : arr) {
            s = "GG";
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);//
        }
        Integer[] intArr = new Integer[]{12,34,5,6};
        for (Integer integer : intArr) {
            integer = 23;
        }
        for (int i = 0; i < intArr.length; i++) {
            System.out.println(intArr[i]);//
        }
        int[] arrInt = new int[]{1,2,3,4};
        for (int i : arrInt) {
            i = 90;
        }
        for (int i : arrInt) {
            System.out.println(i);//
        }
        //person是一个全新的变量,我们给它赋值,并不会影响数组或集合中的元素
        Person[] pArr = new Person[]{new Person("张三",12),new Person("李四",43)};
        Person wangWu = new Person("王五", 18);
        for (Person person : pArr) {
            person = wangWu;
        }
        for (Person person : pArr) {
            System.out.println(person);//
        }

    }
6、关于List接口
  1. List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引
  2. List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
  3. JDK API中List接口的实现类常用的有:ArrayList、LinkedList和Vector
  4. 主要方法:
    1. List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法
    2. void add(int index, T t):在index位置插入t元素
    3. boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
    4. T get(int index):获取指定index位置的元素
    5. int indexOf(T t):返回obj在集合中首次出现的位置
    6. int lastIndexOf(T t):返回obj在当前集合中末次出现的位置
    7. T remove(int index):移除指定index位置的元素,并返回此元素
    8. T set(int index, T t):设置指定index位置的元素为t
    9. List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex 位置的子集合
7、List接口实现类一:ArrayList
ArrayList list = new ArrayList()//底层创建了Object[] elementDate 初始化为 {}
list.add();//第一次调用add() 方法的时候才会创建长度为10的数组,并将add()中的数据添加进去
......
list.add();//如果此次添加导致底层elementDate数组容量不够,则扩容,默认情况下,扩容为原来的1.5倍,
		   //同时将原来的数组中的数据复制到新的数组中
/**
 * 注意:
 * 		并不是前十次添加就不进行数组的长度是否足够的比较,而是而是每一次调用添加操作时都会进行比较操作,
 * 	 若数组容量够用就不进行扩容,若数组长度不够,才会进行扩容操作
 */
8、List接口实现类二:LinkedList
LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
list.add();//创建了Node对象,添加数据进去 调用了linklast() 方法
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
//其中Node定义为:体现了LinkedList的双向链表的特点
private static class Node<E> {
	E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
		this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
9、List接口实现类三:Vector
  • JDK7 和 JDK8 通过Vector构造器创建对象时,底层都创建了长度为10的数组,在扩容方面,默认扩容为原来数组长度的2倍
10、ArrayList、LinkedList、Vector三者的异同
  1. ArrayList和LinkedList的异同

    二者都线程不安全,相对线程安全的Vector,执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于 随机访问get和set,ArrayList觉得优于LinkedList,因LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据

  2. ArrayList和Vector的区别

    Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大 小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack

11、Set接口实现类之一:HashSet
  1. HashSet中的数据都是无序型和不可重复性

  2. 无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加的,而是根据hash值添加的

  3. 不可重复性:保证添加的元素按照equals()判断时,不能返回true,即相同的元素只能添加一次

  4. 添加元素的过程:以HashSet为例:

    1. 向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值
    2. 根据哈希值通过某种算法,计算出元素a在HashSet底层数组中的存放位置(即:索引位置)
    3. 判断数组此位置是否已经有元素:
      1. 如果此位置没有元素,则元素a添加成功
      2. 如果此位置上有其他元素b(或以链表存在的多个元素),再比较元素a和元素b(或以链表存在的多个元素)的hash值
        1. 如果hash值不相同,则元素a,通过链表的形式存放在此位置上
        2. 如果hash值相同,进而需要调用元素a所在类的equals()方法判断
          1. 如果a.equals(b) 返回 true,则元素a添加失败
          2. 如果a.equals(b) 返回 false,则元素a添加成功
    4. 注:
      1. Hash值相同不代表元素相同
      2. Hash值不同不代表存放在数组中的位置不同
  5. HashSet的底层是(底层实际上是HashMap) 数组 + 链表的形式 ,当调用第一次添加方法时创建长度为16的底层数组

  6. Set中的方法都是父接口Collection() 中声明的方法

image-20210907150442456

12、Set接口实现类二:TreeSet
  1. 向TreeSet中添加的数据,必须是相同类型的对象
  2. 两种排序方式:自然排序(实现Comparable接口) 和 定制排序
  3. 自然排序中,比较两个对象是否相同的标准为:compareTo()返回的是否为0,不在是equals()方法
  4. 定制排序中,比较两个对象是否相同的标准为:compare返回的是否为0,不再是equals() 方法
  5. 使用无参的构造器,是自然排序,使用带参的构造器,是定制排序
13、Set接口实现类三:LinkedHashSet
  1. LinkedHashSet是HashSet的子类
  2. LinkedHashSet根据元素的hash值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入的顺序保存起来的
  3. LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能
  4. 在这里插入图片描述
14、关于HashSet的一道题
    /**
     * 判断输出结果
     * Student 已经重写了 hashCode() 和 equals()方法
     * 提示:删除操作也是先根据hash值去查找再进行删除的,name被修改后,hash值就不一致了呀
     */
    @Test
    public void test4(){
        HashSet set = new HashSet();
        Student p1 = new Student(1001,"AA");
        Student p2 = new Student(1002,"BB");
        set.add(p1);
        set.add(p2);
        p1.name = "CC";
        set.remove(p1);//
        System.out.println(set);
        set.add(new Student(1001,"CC"));
        System.out.println(set);//
        set.add(new Student(1001,"AA"));
        System.out.println(set);//
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值