List 接口常用实现类底层分析

一、集合

1.1 简介

        集合主要分为两组(单列集合、双列集合),Collection 接口有两个重要的子接口 List Set,它们的实现子类都是单列集合。Map 接口的实现子类是双列集合,存放的是 K-V

1.2 关系图

二、Collection 接口和常用方法

public interface Collection extends Iterator

2.1 特点

        1、Collection 实现子类可以存放多个元素,每个元素可以是 Object

        2、有些 Collection 的实现类可以存放重复元素,有些不可以。

        3、有些 Collection 的实现类是有序的(List),有些不是有序的(Set

        4、Collection 接口没有直接的实现子类,是通过它的子接口 Set List 来实现的

2.2 常用方法

        下面使用实现类 ArrayList 来演示,如下

public class CollectionTest {
    public static void main(String[] args) {
        Collection list = new ArrayList();
        // add: 添加单个元素
        list.add("java");
        list.add(10);
        list.add(true);
        System.out.println("list="+list);

        // remove: 删除指定元素
        list.remove(true);
        System.out.println("list="+list);
        
        // contains: 查找元素是否存在
        System.out.println(list.contains("java"));
        
        // size: 获取元素个数
        System.out.println("现在集合的大小为:"+list.size());
        
        // isEmpty: 判断是否为空
        System.out.println("判断集合是不是空的"+list.isEmpty());
        
        // clear: 清空集合
        list.clear();
        System.out.println("我要清空集合了,现在集合的大小为:"+list.size());

        // addAll: 添加多个元素
        ArrayList list2 = new ArrayList();
        list2.add("苹果");
        list2.add("香蕉");
        list.addAll(list2);
        System.out.println("添加完多个元素后集合的大小为:"+list.size());
        
        // containsAll: 查找多个元素是否都存在
        System.out.println("查找多个元素是否都存在:"+list.containsAll(list2));
        
        // removeAll: 删除多个元素
        list.removeAll(list2);
        System.out.println("删除多个元素后集合的大小为:"+list.size());
    }
}

2.3 接口遍历

2.3.1 Iterator 方式

        Iterator 对象称为迭代器,主要用于遍历 Collection 集合中的元素。所有实现了 Collection 接口的集合类都有一个 iterator() 方法,用于返回一个实现了 Iterator 接口的对象,即可以返回一个迭代器。

        需要注意的是,在调用 iterator.next() 方法之前必须要调用 iterator.hasNext() 方法进行检测,若不调用最终会报异常。

        如果希望再次遍历,则需要重置我们的迭代器,即重新调用下 coll.iterator() 方法即可。

// 得到一个集合的迭代器
Iterator iterator = coll.iterator();
// 判断是否还有下一个元素
while(iterator.hasNext()){
	// next() 方法有两个作用:下移并且将下移以后集合位置上的元素返回
	System.out.println(iterator.next());
}

2.3.2 for 循环方式

        增强 for 循环,可以代替 iterator 迭代器。它就是简化版的 iterator,本质是一样的,只能用于遍历集合或数组。

for(元素类型 元素名:集合或数组名){

    // 访问元素
}

三、List 接口和常用方法

3.1 特点

        1、List 集合类中元素有序(即添加顺序和取出顺序是一致的)、且可重复。

        2、List 集合中每个元素都有其对应的顺序索引,即支持索引。

        3、List 容器中的元素都对应一个整数型的序号记录其在容器中的位置,可以根据序号存取容器中的元素。

3.2 常用实现类

        ArrayListLinkedListVector

3.3 常用方法

public class ListMethod {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("苹果");
        list.add("香蕉");

        // add(int index,Object ele): 在 index 位置插入 ele 元素
        list.add(1,"西瓜");
        System.out.println("list="+list);

        // addAll(int index,Collection els):从 index 位置开始将 els 中的所有元素添加进来
        List list2 = new ArrayList();
        list2.add("足球");
        list2.add("篮球");
        list.addAll(0,list2);
        System.out.println("list="+list);

        // Object get(int index):获取指定 index 位置的元素
        System.out.println(list.get(0));

        // int indexOf(Object obj):返回 obj 在集合中首次出现的位置
        System.out.println(list.indexOf("足球"));
        
        // int lastIndexOf(Object obj):返回 obj 在集合中末次出现的位置
        System.out.println(list.lastIndexOf("篮球"));
        
        // Object remove(int index):移除指定 index 位置的元素,并返回此元素
        System.out.println(list.remove(0));
        
        // Object set(int index,Object obj):设定指定 index 位置的元素为 obj,相当于是替换
        list.set(2,"美女");
        System.out.println("list="+list);
        
        // List subList(int fromIndex,int toIndex):返回从 fromIndex 到 toIndex 位置的子集合,左闭右开
        List list3 = list.subList(2,3);
        System.out.println("list3="+list3);
    }
}

3.4 排序方法

// 按照 Book 类的 price 属性从小到大排序
public static void sort(List list){
	int size = list.size();
	for(int i=0;i b2.getPrice()){
				list.set(j,b2);
				list.set(j+1,b1);
			}
		}
	}
}

四、ArrayList 

4.1 特点

        ArrayList 可以添加 null,并且可以存储多个。底层是由数组来实现数据存储的,ArrayList 基本等同于 Vector,但是 ArrayList 是线程不安全的。

        ArrayList 中维护了一个 Object 类型的数组 elementData,如下:

// transient 表示该属性不会被序列化
transient Object [] elementData

4.2 无参扩容分析

        当创建 ArrayList 对象时,如果使用的是无参的构造器,则初始化 elementData 容量为 0,第一次添加元素的时候,elementData 会扩容为 10,如需要再次扩容,则扩容 elementData 1.5 倍。

public class ArrayListTest {
    public static void main(String[] args) {
        // 使用无参构造器创建 ArrayList 对象
        // 创建一个容量为 0 的 elementData 数组
        ArrayList list = new ArrayList();
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            // 如果进行扩容,则第一次扩容为10,第二次即以后按照 1.5 倍扩容
            list.add(i);
        }
        for(int i=11;i<=15;i++) {
            // 此时要进行第二次扩容为: 10+10/2 = 15
            list.add(i);
        }
        // 此时要进行第三次扩容为: 15+15/2=22
        list.add(100);
        list.add(200);
        list.add(null);
    }
}

4.3 有参扩容分析

        如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小,如果需要扩容,则直接扩容 elementData 1.5 倍。

public class ArrayListTest2 {
    public static void main(String[] args) {
        // 使用有参构造器创建 ArrayList 对象
        // 创建一个容量为 8 的 elementData 数组
        ArrayList list = new ArrayList(8);
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            // 当 i=9 的时候需要进行扩容,此时按照 1.5 倍扩容:8+8/2 = 12
            list.add(i);
        }
        for(int i=11;i<=15;i++) {
            // 当 i=13 的时候,此时要进行第二次扩容为: 12+12/2 = 18
            list.add(i);
        }
        list.add(100);
        list.add(200);
        list.add(null);
    }
}

五、Vector

5.1 特点

        Vector 底层也是一个对象数组,它是线程安全,Vector 类的操作方法带有 synchronized 关键字修饰。当涉及到线程安全时,可以使用 Vector

5.2 无参扩容分析

        当创建 Vector 对象时,如果使用的是无参的构造器,则初始化 elementData 容量为 10,满了之后,扩容为 elementData  2 倍。

public class VectorTest {
    public static void main(String[] args) {
        // 使用无参构造器创建 Vector 对象
        // new Vector() 时会创建一个容量为 10 的 elementData 数组
        Vector list = new Vector();
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            list.add(i);
        }
        // 此时要进行第二次扩容为: 10+10=20
        list.add(100);
    }
}

5.3 有参扩容分析

        如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小,满了之后,扩容为 elementData  2 倍。

public class VectorTest {
    public static void main(String[] args) {
        // 使用有参构造器创建 Vector 对象
        // new Vector() 时会创建一个容量为 7 的 elementData 数组
        Vector list = new Vector(7);
        for(int i=1;i<=10;i++){
            // add() 方法:先判断是否需要扩容,然后再执行赋值
            // 当 i=8 的时候需要进行第一次扩容,容量为:7+7= 14
            list.add(i);
        }
        list.add(100);
    }
}

六、LinkedList

6.1 特点

        LinkedList 底层实现了双向链表和双端队列的特点,可以添加任意元素,包括 null,线程不安全,没有实现同步。

6.2 底层结构

        1、LinkedList 底层维护了一个双向链表。

        2、LinkedList 中维护了两个属性 first last 分别指向首节点和尾节点。

        3、每个节点(Node 对象),里面又维护了 prevnextitem 三个属性,其中通过 prev 指向前一个,通过 next 指向后一个节点。最终实现双向链表。

        4、所以 LinkedList 的元素的添加和删除不是通过数组完成的,相对来说效率较高。

6.3 LinkedList 和 ArrayList 比较

底层结构 增删的效率 改查的效率
ArrayList 可变数组 较低,数组扩容 较高
LinkedList 双向链表 较高,通过链表追加 较低

如何选择 ArrayList LinkedList

        1、如果我们增删的操作多,选择 LinkedList

        2、如果我们改查的操作多,选择 ArrayList

        3、一般来说,在程序中,80%90% 都是查询,因此大部分情况下会选择 ArrayList

        4、在一个项目中,根据业务灵活选择,有可能是一个模块使用的是 ArrayList,另外一个模块是 LinkedList。也就是说,要根据业务来进行选择

你可能感兴趣的:(java,基础,java)