集合框架总结--Collection和List接口

类集JDK1.2

概念:用来保存数据的集合
作用:可以解决数组的定长问题
顶层接口:Collection,可以理解为一个动态数组,只要长度不够了就能扩容。

Collection接口

Collection接口的继承关系:
在这里插入图片描述
Collection接口是java中保存单个对象的最顶层接口,其下有两个子接口:List和Set;

List接口:允许数据重复
Set接口:不允许数据重复

一般不推荐直接使用Collection接口,因为他不能区分数据到底是否重复,而且方法很局限。

链表:方便元素的插入和删除
数组:方便元素的查看和修改

Collection接口下的常用操作方法:

先来看看都有些什么方法:
在这里插入图片描述
然后着重列一下常用的方法:

  1. 添加一个元素:boolean add(E e);
  2. 删除一个元素:boolean remove(Object o);
  3. 查找一个元素:boolean contains(Object o);
  4. 取得集合大小: int size();
  5. 将集合类转为数组:Object[] toArray();
  6. 取得类集的迭代器:Iterator iterator();
Collection接口下的输出:
  • 使用Iterator迭代器输出(Collection的迭代器接口,专门用来输出Collection接口内容)
    Iterator iterator();取得该Collection接口对象的迭代器
    boolean hasNext();判断当前集合是否还有未遍历的元素
    E next();取得当前正在遍历的内容
    标准格式:
while(iterator.hasNext()) {
	E e  = iterator.next();
}
  • foreach,实际上是Iterator的迭代输出
    Collection接口能使用foreach输出的本质在于,所有子类都实现了Iterator接口

  • ListIterator(双向迭代输出-只有List接口有)
    boolean hasPrevious();从后向前,前提:必须先从前到后输出一次
    E previous();取得前一个元素

  • Enumeration(枚举输出-只有Vector及其子类Stack才有)
    boolean hasMoreElements();
    E nextElement();

  • 快速失败策略(fail-fast);在迭代输出过程中删除时抛出异常:java.util.ConcurrentModificationException

  • 什么是快速失败:优先考虑异常情况,当异常情况发生时,直接向用户抛出异常,程序终止。

  • fail-fast作用:保证集合在多线程场景下读到正确的值;在java.util.包下的集合类大多都会采用此策略报出异常(ArrayList,Vector,LinkedList,HashSet,这些类读写都在同一个副本中

  • fail-safe读写不在一个副本里;java.util.concurrent(CopyOnWriteArrayList采用读写分离,所有的读为异步操作,写为同步操作,且读写不在一个副本/concurrentHashMap)下的都是并发安全集合,不会出异常。

  • ConcurrentModificationException怎么产生的:由于在迭代输出集合时,并发的修改了集合的结构
    protected transient int modCount = 0;//记录当前集合被修改(add,remove等方法)的次数
    在迭代器的内部:取得集合迭代器的时候modCount值: int expectedModCount = modCount;
    当每次调用next方法取得集合的下一个元素的时候,要先判断一下modCount有没有被修改,被修改了就要报错,代码:

final void checkForComodification() {
	if(modCount != expectedModCount) {
			throw new ConcurrentModificationException();
	}
}
	如何避免ConcurrentModificationException:
		1.迭代输出时,不要修改集合的内容
		2.使用迭代器的修改方法(迭代器的修改方法里会重新赋值expectedModCount = modCount)
看一下List接口:80%会用到List

继承关系:
在这里插入图片描述
除了Collection里的方法外还有两个重要方法:

  • 根据下标返回元素:E get(int index);
  • 根据下标修改元素,返回修改前的元素值:E set(int index, E element);
import java.util.ArrayList;
import java.util.List;
public class TestList {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(1);
        System.out.println(list);
        System.out.println(list.size());
        System.out.println(list.contains(3));
        System.out.println(list.get(1));
        System.out.println(list.set(1,7));
        System.out.println(list.get(1));
        System.out.println(list.remove(2));
        System.out.println(list);
        System.out.println(list.remove(new Integer(4)));
        System.out.println(list);
    }
}

结果:

[1, 2, 3, 4, 1]
5
true
2
2
7
3
[1, 7, 4, 1]
true
[1, 7, 1]

删除元素值的对象:boolean remove(Object o);
删除对应下标的元素:E remove(int index);

支持元素重复;
index和数字下标相同,从0开始编号;

  • List集合中添加自定义的类的时候:
import java.util.LinkedList;
import java.util.List;
class Person{
    private String name;
    private Integer age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

public class PersonTest {
    public static void main(String[] args) {
        List<Person> people = new LinkedList<>();
        Person person1 = new Person("张三",25);
        Person person2 = new Person("李四",35);
        Person person3 = new Person("王五",45);
        people.add(person1);
        people.add(person2);
        people.add(person3);
        System.out.println(people.contains(new Person("张三",25)));
    }
}

结果:

false

contains和remove方法需要equals方法的支持,在自定义的类中加入下面覆写的equals代码;

 @Override
    public boolean equals(Object obj) {
        if(this == obj){
            return true;
        }
        if(!(obj instanceof Person)){
            return false;
        }
        Person per = (Person)obj;
        return this.age.equals(per.age)&&this.name.equals(per.name);
    }

结果:

true

如果在idea中写代码时导入lombok.Data包,在自定义类前面使用@Data注解,就可以不用自己写geter,serter和equals方法,maven依赖的下载网址:https://mvnrepository.com/tags/maven

List接口下三个常用的子类

这三个子类的关系与区别(源码)
使用上完全一样,具体的实现细节上有什么不同。
动态数组:数据放不下的时候自动扩容。
基于链表的动态数组

  • ArrayList:使用场景:只查(数组随机访问效率高)
    • 版本:1.2

    • 底层:数组实现
      在这里插入图片描述

    • 构造方法:

      • 无参构造:
        在这里插入图片描述
        DEFAULTCAPACITY_EMPTY_ELEMENTDATA是什么:在这里插入图片描述
        可以看到在构造方法中并没有开辟数组空间,呢在什么时候开辟的数组空间呢,有没有可能在添加的方法中开辟呢,我们来看看添加的方法:
        在这里插入图片描述
        ensureCapacityInternal可能是用来确保数组的内存的函数:
        在这里插入图片描述
        里面调用了ensureExplicitCapacity函数:
        在这里插入图片描述
        里面又调用了grow方法:是扩容函数
        新容量 = 就旧容量+一半旧容量;在这里插入图片描述

ArrayList在初始化的时候采用Lazy-Load(懒加载策略),数组不会在new的时候初始化,只有在new的对象第一次被使用时(add方法),内部的数组才会初始化为长度为10的数组。

扩容策略:每次扩容为原先数组的1.5倍

线程安全性(多线程并发修改内容时,不会产生数据异常):没有锁,所以是线程不安全集合;

  • Vector
    Vector中有一个特殊的子类:Stack:jdk内置的栈;
    以后写栈用Stack;

    • 版本:1.0

    • 底层:数组
      在这里插入图片描述

    • 初始化策略:当产生Vector对象时,内部数组就初始化为大小为10的数组;
      无参构造:
      在这里插入图片描述

    • 扩容机制每次扩容为原先数组的2倍
      add方法:
      在这里插入图片描述
      ensureCapacityHelper函数:
      在这里插入图片描述
      grow:
      新空间 = 旧空间+旧空间(一般不会传capacityIncrement) 在这里插入图片描述

    • 线程安全性
      首先如果他是线程安全的,版本是1.0说明了不可能用lock,lock是1.5才出来的;
      看到上面的add方法是使用synchronized的同步方法来确保线程安全的
      Vector效率低:
      为什么效率低:用了synchronized的同步方法,锁住了对象下的所有方法,使其他的线程全部阻塞。

  • LinkedList:使用场景为:有频繁的插入和删除
    特殊:LinkedList是jdk内置的队列接口(Queue)的子类
    以后写队列用LinkedList
    在这里插入图片描述

    • 版本:1.2
    • 底层:基于双向链表的动态数组
      在这里插入图片描述
    • 线程安全:线程不安全集合
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值