List集合

简介: Collection接口没有提供直接的实现类,而是提供了更加具体的子接口的实现类,其中一个最常用的子接口就是List接口。**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

目录
相关文章
|
3月前
|
存储 安全 Java
【Java集合类面试二十五】、有哪些线程安全的List?
线程安全的List包括Vector、Collections.SynchronizedList和CopyOnWriteArrayList,其中CopyOnWriteArrayList通过复制底层数组实现写操作,提供了最优的线程安全性能。
|
3月前
|
安全
List集合特有功能
List集合特有功能
38 2
|
3月前
|
Java
【Java集合类面试二十三】、List和Set有什么区别?
List和Set的主要区别在于List是一个有序且允许元素重复的集合,而Set是一个无序且元素不重复的集合。
|
3月前
|
存储 Java
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
|
17天前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
46 5
|
1月前
|
存储 分布式计算 NoSQL
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
大数据-40 Redis 类型集合 string list set sorted hash 指令列表 执行结果 附截图
24 3
|
2月前
|
NoSQL Java Redis
List集合按照由小到大排序或者由大到小排序
List集合按照由小到大排序或者由大到小排序
20 3
|
3月前
|
存储 安全 Java
java集合框架复习----(2)List
这篇文章是关于Java集合框架中List集合的详细复习,包括List的特点、常用方法、迭代器的使用,以及ArrayList、Vector和LinkedList三种实现类的比较和泛型在Java中的使用示例。
java集合框架复习----(2)List
|
3月前
|
存储 安全 Java
java集合框架复习----(4)Map、List、set
这篇文章是Java集合框架的复习总结,重点介绍了Map集合的特点和HashMap的使用,以及Collections工具类的使用示例,同时回顾了List、Set和Map集合的概念和特点,以及Collection工具类的作用。
java集合框架复习----(4)Map、List、set
|
3月前
|
Java
用JAVA架建List集合为树形结构的代码方法
这段代码定义了一个表示树形结构的 `Node` 类和一个用于构建树形结构的 `TreeController`。`Node` 类包含基本属性如 `id`、`pid`、`name` 和 `type`,以及子节点列表 `children`。`TreeController` 包含初始化节点列表并将其转换为树形结构的方法。通过过滤和分组操作实现树形结构的构建。详情可见:[代码示例链接1](http://www.zidongmutanji.com/zsjx/43551.html),[代码效果参考链接2](https://www.257342.com/sitemap/post.html)。
40 5
下一篇
无影云桌面