List集合

举报
别团等shy哥发育 发表于 2023/01/08 17:58:07 2023/01/08
【摘要】 @toc 1、List集合  Collection接口没有提供直接的实现类,而是提供了更加具体的子接口的实现类,其中一个最常用的子接口就是List接口。List集合中的元素是有序、可重复的。   List集合关心集合是否有序,而不关心元素是否重复。 1.1 List接口的方法  List除可以从Collection集合继承的方法,List集合中还添加了一些根据索引来操作集合的方法。之前我们说...

@toc

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:添加元素:

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

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

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

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

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

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

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

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

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

  上面运行结果是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类作为普通列表形式使用的示例代码。

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

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

堆栈方法 等效Deque方法
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst()

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

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

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

Queue方法 等效Deque方法
add(e) addLast(e)
offer(e) offerLast(e)
remove() removeFirst()
poll() pollFirst()
element() getFirst()
peek() peekFirst()

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

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

  每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式则会返回一个特殊值,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则表示此对象不在堆栈中
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

1.4 List集合的遍历

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

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

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

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():返回列表中后一个元素的索引。
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

1.4.3 foreach循环遍历

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

1.4.4 Iterator迭代器遍历

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

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。