聊聊集合那些事 之 List集合

集合Collection

    我们看下边这张集合的关系图,橙色的是接口,蓝色的是实现类。下边我们将对这些接口和实现类进行一一介绍:

集合的概述

    集合和数组一样,都是java提供的一种容器。集合不能存储基本数据类型数据,只能用来存储对象,但是它可以用来存储多个不同类型的数据,下面我们看下集合和数组的区别。

集合和数组的区别

  • 数组:长度固定不可变,同一数组只能存储同一类型的元素。
  • 集合:长度可变,同一集合可以存储不同类型的元素。

    在实际开发中,数组一般存储基本数据类型,集合一般存储对象,因为集合不能存储基本数据类型的元素,集合存储基本数据类型的数据需要使用基本数据类型的包装类。

 

集合框架

    集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection和双列集合java.util.Map

    Collection 是单列集合的根接口,用于存储一系列符合某种规则的元素,他有三个重要的子接口,分别是 List接口、Set接口、Queue接口(不常见)。其中 List集合的特点是 元素有序、可重复,Set集合的特点是 元素无序、不可重复。

    Map 是双列集合的根接口,用于存储一系列的键值对(key-value),一组键值对是 Map集合的一个元素,通过键可以找到对应的值。其中值得注意的是:Map集合中的 键不可重复,值可重复,一个键只对应一个值。

 

Collection常用方法

    Collection是单列集合的根接口,所以它定义了一些Set接口和List接口的常用方法,Collection根接口的方法可以操作所有的单列集合。

  • public boolean add(E e): 将指定的对象添加到集合中。

  • public boolean remove(E e): 从集合中删除指定的元素。

  • public boolean contains(E e): 判断当前集合中是否包含指定的对象元素。

  • public boolean isEmpty(): 判断当前集合是否为空,为空返回true。

  • public int size(): 返回集合中元素的个数。

  • public Object[] toArray(): 把集合中的元素,存储到数组中。

  • public void clear() : 清空集合中所有的元素。

  • Iterator<E> iterator(): 返回 Collection集合的迭代器

  •  

Iterator迭代器

    Iterator接口被Collection接口所实现,迭代器的作用就是用来遍历集合中的元素的!

  • public E next():返回迭代的下一个元素。

  • public boolean hasNext():如果仍有元素可以迭代,则返回 true。

代码演示

    下面我们通过一段代码,来演示以下Collection集合的常用方法及Iterator迭代器的使用:

public static void main(String[] args) {
        // 创建一个ArrayList集合对象,这里使用了多态,父类的引用指向子类的实现。忘记的同学可以去复习下面向对象三大特性
        Collection collection = new ArrayList();
​
        collection.add("小明");  // 添加 String类型元素
        collection.add(11);     // 添加 int类型元素,这里会被转换成Integer包装类型,因为集合中不能存储基本数据类型
        collection.add(12.2);   // 添加 Double包装类型元素
        collection.add(true);   // 添加 Boolean包装类型元素
​
        System.out.println("判断集合中元素是否为空,为空返回true:" + collection.isEmpty()); // false
        System.out.println("判断集合中是否包含指定元素:" + collection.contains(11)); // true
        System.out.println("返回集合中元素的个数:" + collection.size()); // 4
​
        collection.remove(11); // 删除集合中指定元素
​
        System.out.println("删除元素11后,集合中元素个数:" + collection.size()); // 3
​
        // 以下进行迭代器是使用
        // 获取 collection集合对象的迭代器
        Iterator iterator = collection.iterator();
        // 使用while循环遍历集合中元素
        while (iterator.hasNext()) {  // hashNext() 判断是否存在下一个元素,存在返回true,不存在返回false
            // next() 获取下一个元素,并将指针指向下一个元素
            System.out.println(iterator.next());  // 打印结果:小明 12.2 true
        }
​
        // 将collection集合转换成数组,这里的toArray()方法,在内部使用类copy方法,所以它操作的是collection集合的副本
        Object[] objects = collection.toArray();
        // 遍历数组中元素
        for (Object obj : objects) {
            System.out.println(obj);   // 打印结果:小明 12.2 true
        }
​
        // 清空集合中所有元素
        collection.clear();
​
        System.out.println("清空集合中元素后,查看集合是否为空:" + collection.isEmpty()); // true
    }

 

 

List集合

List介绍

    List接口继承Collection接口,它是单列集合的一个重要分支,我们习惯性的将实现类List接口的对象叫做 List集合。

    List集合的特点是:元素可重复、元素有序;这和数组的特点是很像的,而它的实现类ArrayList,底层正是维护了一个数组。下面 ArrayList 源码解读会给大家详细分析。

    List有以上特点,是因为集合中的所有元素都是以一种线性方式存储的,在程序中可以通过索引来访问集合中的指定元素;并且集合中元素的存储顺序与取出顺序相同,这些都是数组的特点,再次强调,因为 ArrayList的底层正是维护了一个 final Object类型的数组。

List接口特点总结

1、List集合中的元素时存取有序的。比如存入顺序是1、2、3、4,那么存储元素的顺序也是按照1、2、3、4来完成存储的。

2、List集合中的元素可重复。可以通过 equals方法来比较是否是重复元素。

3、List集合带索引,通过索引就可以精确的操作List集合中的元素了,它的索引与数组一个道理。

 

List接口的常用方法

    List接口作为 Collection接口的子接口,他不仅继承了Collection接口中的所有方法,还有自己的一套方法,主要是一些根据索引来操作集合中元素的方法:

  • public void add(int index, E element): (添加元素)将指定的元素,添加到该集合中的指定位置上。

  • public E remove(int index): (删除元素)移除列表中指定位置的元素, 返回的是被移除的元素。

  • public E set(int index, E element): (更新元素)用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

  • public E get(int index): (查找元素)返回集合中指定位置的元素。

 

ArrayList 集合

ArrayList介绍

 java.util.ArrayList集合的数据存储结构是数组结构。元素增删慢,查找快。由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。

    许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。

    java.util.ArrayList大小可变的数组的实现,它是List集合的一个实现类, ArrayList 中可以不断添加元素,其大小也自动增长。

 

ArrayList常用方法

  • public boolean add(E element) : 将元素添加到集合的尾部

  • public boolean add(int index,E element) : 将元素添加到集合的指定位置

  • public E remove(int index) : 删除集合中指定位置的元素

  • public boolean remove(Object o) : 删除集合中首次出现的指定的元素

  • public void clear() : 清空集合中的所有元素

  • public boolean contains(Object o) : 判断集合中是否包含指定的元素,存在返回true

  • public E get(int index) : 返回集合中指定位置的元素

  • public boolean isEmpty() : 判断集合中是否存在元素,如果没有元素返回true

  • public int indexOf(Object o) :返回集合中首次出现指定元素的索引位置,如果不存在返回-1

  • public int lastIndexOf(Object o) : 返回集合中最后一次出现指定元素的索引位置,不存在返回-1

  • public E set(int index,E element) : 用指定元素替换指定位置上的元素,返回被替换的元素值

  • public Object[] toArray() : 将集合转换成数组,操作的是集合的副本,不影响集合本身。

  • public void trimToSize() : 将此 ArrayList 实例的容量调整为列表的当前大小。应用程序可以使用此操作来最小化 ArrayList 实例的存储量。

  • public int size(): 返回集合中元素的数量

 

ArraList源码解读

下面我们来阅读一段 ArrayList的源码,来看看 ArrayList集合是怎么实现数组长度大小可变的。

 // 默认初始容量为10
    private static final int DEFAULT_CAPACITY = 10;
​
    // ArrayList底层维护了一个 final Object类型的空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    // 存储ArrayList元素的数组缓冲区,ArrayList的容量是这个数组缓冲区的长度。
    // 任何空的ArrayList集合,也就是使用无参构造方法创建的ArrayList集合,它的初始容量都是 DEFAULT_CAPACITY,也就是10.
    transient Object[] elementData; 
    
    // 通过构造方法,构建一个初始容量为10的ArrayList空集合。
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
​
    /**
    *   添加元素,我们可以通过源码来了解ArrayList是怎么实现数组长度大小可变的。
    *   ensureCapacityInternal:用来保证集合的容量,防止数组越界的发生
    *   elementData 数组缓冲区,它的长度是ArrayList集合中元素的个数
    */ 
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // size是当前ArrayList集合中元素个数,将元素个数+1,保证集合的容量
        elementData[size++] = e; // 将元素插入到集合的尾部,
        return true;
    }
    
    // minCapacity是集合中存储元素的最小容量,所以每次像集合中插入元素时,都要+1
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 比较 初始容量10 与 集合中实际元素个数,返回他们之中的最大值。以保证集合可以成功扩容并添加元素
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
   大家要养成阅读源码的习惯,这在企业开发中是很重要的,可能开始阅读起来比较困难,只要坚持,你会发现源码其实也没那么神秘。

ArrayList 集合代码演示

public static void testMyArrayList() {
        // 构建一个初始值为10的空ArrayList集合(去看源码)
        List list = new ArrayList();
        System.out.println("查看集合中是否存在元素:" + list.isEmpty());
        System.out.println("查看集合中元素个数:" + list.size());
​
        // 向集合中添加元素
        list.add("aa");     // 添加String类型的元素
        list.add(22);       // 添加Integer类型的元素
        list.add('a');      // 添加Char类型的元素
        list.add(12.22);    // 添加Double类型的元素
​
        /*
        * 根据索引获取集合中的元素
        * 集合中元素的存储顺序与添加顺序是相同的,可以通过索引访问指定位置的元素,一旦索引越界就
        *会报异常。
        */ 
        System.out.println("索引值为0的元素" + list.get(0)); // aa
        System.out.println("索引值为1的元素" + list.get(1)); // 22
        System.out.println("索引值为2的元素" + list.get(2)); // a
        System.out.println("索引值为3的元素" + list.get(3)); // 12.22
        // java.lang.IndexOutOfBoundsException 索引越界异常
        // System.out.println("索引值为4的元素" + list.get(4)); 

​
        // 向集合中指定位置添加元素
        list.add(2, "aa");
        list.add(0, 22);
        list.add(3, 2);
        /*
        *  向集合最后一个位置添加元素,之前最后一个位置的元素应该是12.22,但是我们向最后一个位置
        *添加元素后,这个位置的值被'a'取代了,12.22会向后移动一位
        *  所以集合中的元素排序应该是 最后一位是12.22,倒数第二位是‘a’,我们来查看一下对不对
        */ 
        list.add(list.size() - 1, 'a'); // 向当前集合最后一个位置添加元素a
        // 22,因为我们向索引0位置添加了元素22,所以返回值是22
        System.out.println("索引值为0的元素" + list.get(0)); 
        System.out.println("当前集合中元素个数:" + list.size()); // 8
        System.out.println("索引值为6的元素(最后一位元素)" + list.get(7)); // 12.22
        // 返回值是 a 结果证明我们的猜想是对的
        System.out.println("索引值为5的元素(倒数第二位元素)" + list.get(6)); // a 
​
        // 遍历list集合中的元素
        for (Object obj : list) {
            System.out.println(obj); // 当前集合中元素的顺序是 :22 aa 22 2 aa a a 12.22
        }
​
​
        /*
         *  remove(int index)、remove(Object o)
         *  当前集合中元素的顺序是 :22 aa 22 2 aa a a 12.22
         *  值得注意的是,remove方法有两个重载方法,一个是移除指定索引位置的元素,一个是移除第一
         *次出现在集合中的指定元素.
         *  当要被移除的元素是整数时,list集合会调用按索引删除的remove方法。
         *  当我们要删除集合中的整数时,需要先使用找到要删除元素的索引,然后按索引删除可。
         *  以上是包子自己发现的一个有趣的结论,可能不正确,知道内部原理的大佬欢迎留言指正,包子
         *会及时进行修改
         *
         * */
        // 删除元素中第一个出现的元素22
        int i = list.indexOf(22);// 元素22第一次出现的索引位置
        // 返回值是被删除的元素值,22
        System.out.println("根据索引删除第一个出现的元素2:" + list.remove(i)); 
        // 删除元素中最后一个出现的元素22
        int j = list.lastIndexOf(22);// 元素22最后一次出现的索引位置
        // 返回值是被删除的元素值,22
        System.out.println("根据索引删除第一个出现的元素2:" + list.remove(j)); 
​
​
        // 替换指定位置的元素值
        System.out.println("集合中索引为3的位置的元素是:" + list.get(3));
        System.out.println("将集合中索引为3的位置的元素替换成‘哈哈’,返回值是被替换的元素值:" + list.set(3, "哈哈"));
        System.out.println("替换后集合中索引值为3的元素是:" + list.get(3));
​
        // 查看集合中是否包含元素 "哈哈",存在返回true,否则返回false
        System.out.println("集合中是否存在元素‘哈哈’?" + list.contains("哈哈")); //true
​
        // 清空集合中所有元素
        list.clear();
        System.out.println("被清空后集合中元素数量:" + list.size()); // 0
    }

 

LinkedList集合

java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。

在学习LinkedList集合之前,我们先看一下什么是双向链表?

链表结构是数据结构的一种,和数组一样,它是一种物理结构的数据结构,数据结构分为两大类:物理结构、逻辑结构。

物理结构:就是按照这种结构真实的在内存中存储数据的结构,物理结构的数据结构只有 链表和数组两种,这是最基本的数据结构。

逻辑结构: 是一种假想的不是真实存在的,但是能够更好的帮助我们完成对数据的操作的结构,就像我们的业务逻辑一样。

关于数据结构,包子会单独写一篇文章来说明,这里就简单一提,我们还是先来说说 LinkedList集合。

LinkedList介绍

上图中已经很详细的描述类双向链表的结构以及增删查操作,LinkedList集合就是使用双向链表实现的。

实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,LinkedList集合除了继承了List集合的一些对元素的操作方法外,LinkedList提供了大量首尾操作的方法。还这些方法我们作为了解即可:

  • public boolean add(E e): 向集合的末尾添加元素

  • public boolean offer(E e): 将指定元素添加到集合的末尾

  • public boolean add(int index,E e):向集合中指定位置插入指定元素

  • public void addFirst(E e): 将指定元素插入集合的开头

  • public void addLast(E e):将指定元素添加到集合的末尾。

  • public void push(E e): 将元素压入栈中,也就是添加到集合的第一个位置

  • public E pop(): 将元素从栈中弹出,也就是移除并返回集合中第一个元素。

  • public boolean offerFirst(E e):向集合开头插入指定元素

  • public boolean offerLast(E e): 向集合末尾插入指定元素

  • public E poll(): 获取并移除集合中第一个元素

  • public E pollFirst(): 获取并移除集合中第一个元素,如果集合为空返回null

  • public E pollLast(): 获取并移除集合中最后一个元素,如果集合为空返回null

  • public E remove(): 移除并返回集合的第一个元素

  • public E remove(int index): 移除并返回列表中指定索引位置的元素

  • public boolean remove(Object o): 移除集合中首次出现的指定元素,如果存在返回true

  • public E removeFirst(): 移除并返回集合中第一个元素

  • public E removeLast(): 移除并返回集合中最后一个元素

  • public E set(int index,E element): 替换集合中指定位置的元素

  • public ListIterator<E> listIterator(int index): 返回集合的迭代器(顺序迭代)

  • public Iterator<E> descendingIterator(): 返回集合的逆序迭代器(逆序迭代)

  • public E element(): 获取但不移除集合的第一个元素,列表为空抛出异常

  • public E peek(): 获取但不移除集合的第一个元素,如果列表为空,则返回null

  • public E peekFirst(): 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。

  • public E peekLast(): 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null

  • public E get(int index): 返回集合中指定索引位置的元素,超出索引范围抛出异常

  • public E getFirst(): 返回集合中第一个元素,集合为空抛出异常

  • public E getLast(): 返回集合中最后一个元素,集合为空抛出异常

  • public int indexOf(Object o): 返回首次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1

  • public int lastIndexOf(Object o): 返回最后一次出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1

  • public boolean contains(Object o): 如果集合中存在指定元素,则返回true

  • public int size(): 返回集合中元素的总个数

  • public boolean isEmpty():如果列表不包含元素,则返回true。

    LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用(了解即可)。代码的实现同学们自己去实现吧,还是很简单的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值