前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >List集合

List集合

作者头像
别团等shy哥发育
发布2023-02-25 11:10:36
6330
发布2023-02-25 11:10:36
举报
文章被收录于专栏:全栈开发那些事

List集合

1、List集合

  Collection接口没有提供直接的实现类,而是提供了更加具体的子接口的实现类,其中一个最常用的子接口就是List接口。List集合中的元素是有序、可重复的

   List集合关心集合是否有序,而不关心元素是否重复。

1.1 List接口的方法

  List除可以从Collection集合继承的方法,List集合中还添加了一些根据索引来操作集合的方法。之前我们说Collection接口中没有提供修改元素的方法,而List接口中提供了根据元素的下标索引位置来修改元素的方法set,下面列出了List接口新增的方法。

(1)添加元素

  • void add(int index,Object element):在[index]位置添加一个元素。
  • boolean addAll(int index,Collection eles):在[index]位置添加多个元素。

(2)获取元素

  • Object get(int index):获取[index]位置的元素。
  • List subList(int fromIndex,int toIndex):获取[fromIndex,toIndex)范围的元素。

(3)获取元素索引

  • int indexOf(Object obj):获取obj在List集合中首次出现的索引位置,如果不存在则返回-1.
  • int lastIndexOf(Object obj):获取obj在List集合中最后出现的索引位置,如果不存在则返回-1.

(4)删除和替换元素

  • Object remove(int index):删除[index]位置的元素。
  • Object set(int index,Object element):替换[index]位置的元素为element。

  因为List接口是Collection接口的子接口,因此之前Collection接口的方法,List接口也同样适用,Collection集合的遍历方式也同样适用于List接口的集合。

1.2 案例:元素的增删改查

  案例1:添加元素:

代码语言:javascript
复制
public class ListAddTest {
    public static void main(String[] args) {
        ArrayList<Object> list = new ArrayList<>();
        list.add("张三");
        list.add(0,"李四");//把“李四”添加到索引[0]位置
        list.add(1,"王五");
        for (Object o : list) {
            System.out.println(o);
        }
    }
}
image-20221003134119908
image-20221003134119908

  案例2:获取指定位置元素

代码语言:javascript
复制
public class ListGetTest {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");

        Object o = list.get(1);
        System.out.println(o);
    }
}
image-20221003134334931
image-20221003134334931

  案例3:获取指定位置元素索引位置

代码语言:javascript
复制
public class ListGetTest {
    public static void main(String[] args) {
        listIndexOfTest();
    }
    public static void listIndexOfTest(){
        ArrayList<Object> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("李四");

        //从[0]开始查找,返回第一次出现的"李四“的索引
        int index = list.indexOf("李四");
        System.out.println("index="+index);
    }
}
image-20221003134431289
image-20221003134431289

  案例4:删除指定[0]位置的元素“张三”

代码语言:javascript
复制
import java.util.ArrayList;

public class ListRemoveTest {
    public static void main(String[] args) {
        ArrayList<Object> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");

        list.remove(0);//删除索引为[0]的元素。
        list.forEach(System.out::println);
    }
}
image-20221003134656624
image-20221003134656624

  案例5:删除指定元素“2”

代码语言:javascript
复制
public class ListRemoveTest2 {
    public static void main(String[] args) {
        ArrayList<Object> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        list.remove(2);
        list.forEach(System.out::println);
    }
}
image-20221003134844833
image-20221003134844833

  上面运行结果是1,2,4,而不是1,3,4,这是因为现在List接口中有两个remove方法,一个是remove(Object obj),另一个是remove(int index),而上面代码中list.remove(2)匹配的是remove(int index),删除的是[2]位置的元素3.   因为添加到集合中的1,2,3,4已经自动装箱为Integer的对象了,所以如果要删除元素2,那么可以通过list.remove(Integer.valueOf(2))的方法实现或使用迭代器配合equals判断是否是2再删除。

1.3 List接口的实现类

  List接口的实现类都具备List接口有序且可以重复的特点,使用方式完全一样,仅仅是底层存储结构不同。这里列举了List接口中比较“著名”的几个实现类。

  • ArrayList类:动态数组。
  • LinkedList:双向链表,JDK1.6之后又实现了双端队列Deque接口。
  • Vector类:动态数组。
  • Stack类:堆栈/

1.3.1 ArrayList类

  ArrayList类是使用最频繁的List集合类之一,它其实就是我们之前反复提到的动态数组的实现,因此它底层的物理结构是数组。

  之前使用的数组是静态分配空间,一旦分配了空间大小,就不可再改变;而动态数组是动态分配空间,随着元素的不断插入,它会按照自身的一套机制不断扩充自身的容量。动态数组扩容并不是在原有连续的内存空间后进行简单的叠加,而是重新申请一块更大的新内存,并把现有容器中的元素逐个赋值过去,然后销毁旧的内存。

  在构建ArrayList集合对象时,如果没有显示指定容量,那在JDK1.6及其之前版本的内部数组初始化容量默认为10,之后的版本初始化容量为长度为0的空数组,在添加第一个元素时再创建一个长度为10的数组。ArrayList延迟创建长度为10的数组的目的是节省内存空间,因为有时我们在创建ArrayList集合对象后,并没有添加元素,这点在方法的返回值类型是List类型时,极有可能存在。当然你也可以在创建ArrayList集合对象时,自己指定初始化容量。

  ArrayList类在添加一个新元素时,如果现有的数组容量不够,则会将新数组长度扩容为原来的1.5倍之后再添加。如果调用addAll方法一次添加多个元素,则会先判断原有数组是否够装,如果不够,则判断1.5倍容量是否够装,如果不够,就按实际需要来扩容数组。

1.3.2 LinkedList类

  LinkedList类是典型的双向链表的实现类,除可以实现List接口的方法,还为在列表的开头及结尾get(获取)、remove(移除)和insert(插入)元素提供了统一的命名方法。这些操作允许将链表用作堆栈、队列或双端队列。

  将LinkedList类作为普通列表形式使用的示例代码。

代码语言:javascript
复制
public class LinkedListTest1 {
    public static void main(String[] args) {
        LinkedList<Object> list = new LinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        for (Object o : list) {
            System.out.println(o);
        }
    }
}
image-20221003145616080
image-20221003145616080

  JDK1.6之后LinkedList类实现了Deque接口。双端队列也可用作LIFO(后进先出)堆栈。如果要使用堆栈的集合,那么可以考虑使用LinkedList类,而不是Deque接口,如下表所示。

堆栈方法

等效Deque方法

push(e)

addFirst(e)

pop()

removeFirst()

peek()

peekFirst()

  将LinkedList类作为堆栈使用的示例代码:

代码语言:javascript
复制
public class LinkedListTest2 {
    public static void main(String[] args) {
        LinkedList<Object> list = new LinkedList<>();
        //入栈
        list.addFirst(1);
        list.addFirst(2);
        list.addFirst(3);

        //出栈:LIFO(后进先出)
        System.out.println(list.removeFirst());
        System.out.println(list.removeFirst());
        System.out.println(list.removeFirst());
        //栈空了,会报异常java.util.NoSuchElementException
        System.out.println(list.removeFirst());
    }
}
image-20221003145922102
image-20221003145922102

  LinkedList类用作队列时,将得到FIFO(先进先出)行为,将元素添加到双端队列的末尾,从双端队列的开头移除元素,LinkedList类作为队列使用的方法如下表所示。

Queue方法

等效Deque方法

add(e)

addLast(e)

offer(e)

offerLast(e)

remove()

removeFirst()

poll()

pollFirst()

element()

getFirst()

peek()

peekFirst()

  将LinkedList类作为队列使用的示例代码:

代码语言:javascript
复制
import java.util.LinkedList;

public class LinkListTest3 {
    public static void main(String[] args) {
        LinkedList<Object> list = new LinkedList<>();
        //入队
        list.add(1);
        list.add(2);
        list.add(3);

        //出队,FIFO(先进先出)
        System.out.println(list.pollFirst());
        System.out.println(list.pollFirst());
        System.out.println(list.pollFirst());
        //队列空了,返回Null
        System.out.println(list.pollFirst());
    }
}
image-20221003150222650
image-20221003150222650

  每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式则会返回一个特殊值,null或false,具体形式取决于操作,LinkedList类作为双向链表使用的方法如下所示。

第一个元素(头部)

最后一个元素(尾部)

抛出异常

特殊值

抛出异常

特殊值

插入

addFirst(e)

offerFirst(e)

addLast(e)

offerLast(e)

移除

removeFirst()

pollFirst()

removeLast()

pollLast()

检查

getFirst()

peekFirst()

getLast()

peekLast()

1.3.3 Vector类

  Vector类是STL(标准模板库)中最常见的容器,也是动态数组数据结构的实现。关于Vector类和ArrayList类两种动态数组的对比,如下表所示。

底层结构

初始化容量

扩容机制

线程安全(同步)

版本

效率

Vector类

动态数组

如果没有显示指定容量,则创建对象时,初始化容量为0

2倍

安全(同步)

较老

较低

ArrayList类

动态数组

如果没有显示指定容量,则在JDK6版本创建对象时,初始化容量为10,在更高版本创建对象时,初始化容量为0,第一次添加元素时,初始化容量为10.

1.5倍

不安全(不同步)

较新

较高

1.3.4 Stack类

  Stack类是Vector的子类,用于表示后进后出(LIFO)的对象堆栈,通过5个操作对Vector类进行了扩展,下表列出了Stack类具有堆栈特点的操作。

方法

功能解释

push(Object e)

将对象插入Stack类的顶部

Object peek()

返回位于Stack类顶部的对象但不将其移除

Object pop()

移除并返回位于Stack类顶部的对象

boolean empty()

堆栈是否为空

int search(Object o)

对象到堆栈顶部的位置,以1为基数;返回值-1则表示此对象不在堆栈中

代码语言:javascript
复制
import java.util.EmptyStackException;
import java.util.Stack;

public class StackTest {
    //添加新元素到栈,即把新元素压入栈,成为新的栈顶元素
    static void showPush(Stack st,Object value){
        st.push(value);
        System.out.println("push("+value+")");
        System.out.println("现在栈顶元素是:"+st.peek());//查看当前栈顶元素
        System.out.println("现在栈中的元素有:"+st);
    }

    //弹出当前栈顶元素,下一个元素称为新的栈顶元素
    static void showPop(Stack st){
        System.out.println("pop->"+st.pop());
        System.out.println("现在栈中元素有:"+st);
    }
    public static void main(String[] args) {
        Stack<Object> st = new Stack<>();
        showPush(st,42);
        showPush(st,66);
        showPush(st,99);
        showPop(st);
        showPop(st);
        showPop(st);
        try {
            showPop(st);
        } catch (EmptyStackException e) {
//            e.printStackTrace();
            System.out.println("empty stack");
        }
    }
}
image-20221003153122266
image-20221003153122266

1.4 List集合的遍历

  因为List集合也属于Collection系列的集合,此前Collection集合支持的foreach遍历和Iterator遍历对于List集合来说仍然适用,这里就不再重复,下面介绍List集合的其他遍历方式。

1.4.1 普通for遍历循环遍历(效率不高)

代码语言:javascript
复制
public class ListForTest {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student(1,"张三"));
        list.add(new Student(2,"李四"));
        list.add(new Student(3,"王五"));
        list.add(new Student(4,"赵六"));
        list.add(new Student(5,"钱七"));

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

    }
}
image-20221003153627730
image-20221003153627730

1.4.2 ListIterator迭代器

  List集合额外提供了一个listIterator()方法,该方法返回ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法,如下所示。

  • void add():通过迭代器添加元素到对应集合。
  • void set(Object obj):通过迭代器替换正在迭代的元素。
  • void remove():通过迭代器删除刚才迭代的元素。
  • boolean hasPrevious():如果逆向遍历列表,则判断往前是否还有元素。
  • Object previous():返回列表中的前一个元素。
  • int previousIndex():返回列表中的前一个元素的索引。
  • boolean hasNext():判断有没有下一个元素。
  • Object next():返回列表中的最后一个元素。
  • int nextIndex():返回列表中后一个元素的索引。
代码语言:javascript
复制
public class ListIteratorTest {
    public static void main(String[] args) {
        ArrayList<Object> c = new ArrayList<>();
        c.add(new Student(1,"张三"));
        c.add(new Student(2,"李四"));
        c.add(new Student(3,"王五"));
        c.add(new Student(4,"赵六"));
        c.add(new Student(5,"钱七"));

        //从指定位置往前遍历
        ListIterator<Object> listIterator = c.listIterator(c.size());
        while (listIterator.hasPrevious()){
            Object previous = listIterator.previous();
            System.out.println(previous);
        }
    }
}
image-20221003154140725
image-20221003154140725

1.4.3 foreach循环遍历

代码语言:javascript
复制
import java.util.ArrayList;

public class ListForTest {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student(1,"张三"));
        list.add(new Student(2,"李四"));
        list.add(new Student(3,"王五"));
        list.add(new Student(4,"赵六"));
        list.add(new Student(5,"钱七"));

        for (Student student : list) {
            System.out.println(student);
        }
    }
}
image-20221003154304518
image-20221003154304518

1.4.4 Iterator迭代器遍历

代码语言:javascript
复制
public class ListForTest {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student(1,"张三"));
        list.add(new Student(2,"李四"));
        list.add(new Student(3,"王五"));
        list.add(new Student(4,"赵六"));
        list.add(new Student(5,"钱七"));
        Iterator<Student> iterator = list.iterator();
        while(iterator.hasNext()){
            Student next = iterator.next();
            System.out.println(next);
        }
    }
}
image-20221003154442902
image-20221003154442902
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • List集合
  • 1、List集合
    • 1.1 List接口的方法
      • 1.2 案例:元素的增删改查
        • 1.3 List接口的实现类
          • 1.3.1 ArrayList类
          • 1.3.2 LinkedList类
          • 1.3.3 Vector类
          • 1.3.4 Stack类
        • 1.4 List集合的遍历
          • 1.4.1 普通for遍历循环遍历(效率不高)
          • 1.4.2 ListIterator迭代器
          • 1.4.3 foreach循环遍历
          • 1.4.4 Iterator迭代器遍历
      相关产品与服务
      容器服务
      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档