2022-08-05 第五小组 顾祥全 学习笔记 day29-集合-Collection集合

集合

什么是集合?

  • 集合其实就是一个容器,用来存放引用类型的数据
  • 使用不同的集合等同于使用不同的数据结构

集合用来存放什么?

  • 集合中存放了引用类型(对象的内存地址),集合不能存储基本数据类型

集合分类

  1. 单个的方式存储元素 ——> java.util.Collection(超级父接口)
  2. 键值对的方式存储元素 ——> java.util.Map(超级符接口)

Collection集合

以单个的方式存储元素

Collection集合继承关系

«interfact»
Collection
«interfact»
Iterable
«interfact»
List
«interfact»
Set
«interfact»
Iterator
«interfact»
SortedSet
ArrayList
LinkedList
Vector
HashSet
TreeSet

Iterator接口

想要迭代集合就得先了解集合的迭代器Iterator

  1. 包路径: java.util.Iterator<E>
  2. 方法:
方法功能
boolean hasNext()判断迭代器指向的集合中是否还有元素可迭代
E next()返回迭代的下一个元素
void remove()从迭代器指向的 collection 中移除迭代器返回的最后一个元素
更新迭代器中保存的集合修改次数
迭代集合

通过Collection集合继承结构图可以看出,Colletion集合实现了Iterable接口,表示所有的集合都是可迭代的,每个集合都有一个iterator()方法,集合调用这个方法会返回一个迭代器(Iterator),下面我们用ArrayList集合来测试一下这个迭代器

 public static void main(String[] args) {
        // 给arrayList初始化容量5
        ArrayList arrayList = new ArrayList(5);
        arrayList.add("1");
        arrayList.add("2");
        arrayList.add("3");


        // 获取集合的迭代器
        Iterator it = arrayList.iterator();
        // 迭代集合,并向控制台输出集合中的元素
        while (it.hasNext()) {
            Object obj = it.next();
            System.out.println(obj);
        }

    }

对于迭代器Iterator的底层工作原理是怎样的呢,我们来看下面的这张图片
在这里插入图片描述

foreach的底层实现是迭代器
package com.jsoft.collection;

import java.util.ArrayList;

public class TestIterator {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");

        // 使用foreach遍历list集合
        for(String str : list) {
            System.out.println(str);
        }
    }
}

上面的代码编译后字节码文件的反编译结果如下,可以看出foreach底层实现是迭代器

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.jsoft.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public Test() {
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("1");
        Iterator it = list.iterator();

        while(it.hasNext()) {
            String eleStr = (String)it.next();
            if ("1".equals(eleStr)) {
                it.remove();
            }
        }

        System.out.println(list);
    }
}

集合结构发生变化时迭代器需要重新获取

使用Collection集合的iterator()方法,会返回一个Iterator的实例(集合内部类Itr的实例),这个实例又被称作为迭代器,这个迭代器实例在实例化时将集合结构修改次数用变量(expectedModCount)保存起来(看下图,Java8)
在这里插入图片描述
可以把这个迭代器看作是一个 “集合快照”,并且在集合结构发生改变时,迭代器对象中的(expectedModCount变量)并未一起更新,所以需要重新获。
如果迭代器并未重新获取,在迭代器遍历集合的下一个元素时(调用next()时),检测到迭代器中保存的修改次数和集合中保存的修改次数不同时,会抛出ConcurrentModificationException(并发修改异常),具体请看下图(Java8)
在这里插入图片描述

使用迭代器迭代集合删除元素时,要使用Iterator的remove()

引子——为什么使用迭代器迭代集合时,不能使用集合中的remove()方法去删除元素?

  1. 使用集合的remove()方法并不会更新此集合迭代器对象中保存的集合的修改次数,使用remove()删除元素后,会在下一次调用next()时会抛出异常

  2. 使用Iterator的remove()方法时,会连同迭代器中保存的修改次数一同更新。(下图Java8)
    在这里插入图片描述

  1. 使用集合中的remove()删除元素
package com.jsoft.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("1");
        // 获取list集合的迭代器对象
        Iterator<String> it = list.iterator();
        // 使用it迭代list集合
        while(it.hasNext()) {
            // 获取迭代器指向的集合的下一个元素
            String eleStr = it.next();
            // 使用集合list的remove()删除所有的"1"
            if ("1".equals(eleStr)) {
                list.remove(eleStr);
            }
        }
        // 向控制台中打印结果
        System.out.println(list);

    }
}

结果如下,在代码的第19行(调用next()时)抛出异常
在这里插入图片描述

  1. 使用Iterator(迭代器)的remove()
package com.jsoft.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("1");
        // 获取list集合的迭代器对象
        Iterator<String> it = list.iterator();
        // 使用it迭代list集合
        while(it.hasNext()) {
            // 获取迭代器指向的集合的下一个元素
            String eleStr = it.next();
            // 使用迭代器it的remove()删除所有的"1"
            if ("1".equals(eleStr)) {
                it.remove();
            }
        }
        // 向控制台中打印结果
        System.out.println(list);

    }
}

结果如下,并未抛出异常
在这里插入图片描述

总结一下
在使用foreach和迭代器遍历/迭代集合时,不要使用集合的remove()方法删除元素

  • 对于迭代器迭代集合时,要使用自身的remove()方法去删除元素
  • 对于foreach遍历集合时,不要删除元素

Colleciton接口

  1. 包路径:java.util.Collection<E>
  2. 方法:
    在这里插入图片描述
contains()和remove()注意点

集合中contains()、remove()方法底层都调用了equals()方法,因为Object中equal()使用“=="进行比较,所以需要重写equals()方法,设置类型实例相等条件

List接口

List集合中存储元素特点:有序1可重复2

  1. 包路径: java.util.List<E>
  2. List接口特有方法
    在这里插入图片描述
ArrayList类
  1. ArrayList集合采用了数组这种数据结构,底层实际是一个Object[]
  2. ArrayList集合默认容量为10在添加第一个元素时将这个默认容量给底层数组
  3. ArrayList集合扩容:扩容到原先的1.5倍
  4. 数组这种数据结构优缺点:
    • 优点:检索元素效率极高,因为数组元素在内存中存储是连续的,并且数组中每个元素所占空间大小都是相同的。我们知道了数组的内存地址,知道了数组元素的下标,知道了数组元素所占字节大小,就可以通过一个数学表达式计算出指定下标元素的内存地址,所以数组检索元素效率极高
    • 缺点:随机增删效率较低,为了保证数组元素在内存中是连续的这种特点,我们随机添加或删除一个元素,会涉及到后面所有元素整体向前或向后位移操作,所以效率较低;不不能存储大容量数据,因为很难在内存空间中找到一份大且连续的空间

ArrayList集合的默认容量
在这里插入图片描述
通过注释我们可以了解到ArrayList底层是elementData(Object[]),并且在添加第一个元素时,任何elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA都将扩展为DEFAULT_CAPACITY
我们来看具体代码
在这里插入图片描述
在这里插入图片描述
结论: ArrayList集合默认容量为10,在添加第一个元素时将这个默认容量给底层数组

LinkedList类
  1. 底层采用了双向链表这种数据结构
  2. 无默认容量
  3. 双向链表这种结构的优缺点
    • 优点:随机增删元素效率较高,由于双向链表中的元素(节点)在内存空间中的内存地址是不连续的,随机增删元素并不涉及到整体元素位移操作,所以效率高
    • 缺点:检索元素效率极低,由于双向链表中的元素(节点)在内存空间中的内存地址是不连续的,想要检索一个元素需要从头节点开始依此遍历,所以效率极低
Vector类
  1. 底层采用了数组这种数据结构
  2. 默认容量为10
  3. 数组扩容:扩容原来的2倍
  4. 与ArrayList的区别 ----> 线程安全(Vector中所有的方法都被synchronized关键字修饰)

已过时,由于Verctor中的方法都被synchronized修饰所以效率较低,现在保证线程安全有其他的方式了。----> 使用java.util.Collections中的静态方法
在这里插入图片描述

Set接口

Set集合存储元素的特点:无序无可重复

  1. 包路径:java.util.Set<E>
  2. 方法 ----> 继承自Collection接口的方法,Set接口无特有方法
HashSet类
  1. HashSet集合底层采用了哈希表这种数据结构
  2. HashSet在实例化时,底层实际实例化了个HashMap
  3. 往HashSet集合中存储元素,实际存放到了HashMap集合的key部分
SortedSet接口

SortSet集合中继承了HashSet接口的特点(也是无序不可重复的),并且SortSet集合中的元素都是可排序的(自动排序)

TreeSet类
  1. TreeSet集合继承了SortedSet接口的特点(无序不可重复可自动排序)
  2. TreeSet集合底层采用了二叉树这种数据结构
  3. TreeSet在实例化时,底层实际实例化了个TreeMap
  4. 往TreeSet集合中存储元素,实际时存放到了TreeMap集合的key部分

UML类图

在这里插入图片描述


  1. 有序是指存储元素时有次序的,进去是什么元素出来还是什么元素,元素可以用下标来表示 ↩︎

  2. 可重复是指之前存入一个A元素,之后还可以存A元素 ↩︎

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值