JAVA List 详细介绍

目录

什么是List

List的特点 (不重要)

如何创建List

List常用方法

区别

ArrayList&LinkedList

Vector

Stack

与Arrays类的联系

小细节


什么是List

继上一篇JAVA Arrays类 详细介绍之后

List:列表类型,表示一个有序集合。
Map:映射类型,表示键值对的集合。
Set:集合类型,表示一组不重复的元素。

以上就是接下来几篇的内容,本篇主要是List

List
├── ArrayList
├── LinkedList
└── Vector——Stack

以上是List接口的几个实现类,以下,则是一些不太常用的List可以先略过,Stack应该还算常用,后面会讲

  1. Stack(栈):继承自Vector类,实现了一个后进先出(LIFO)的栈。
  2. CopyOnWriteArrayList:这是一个线程安全的列表,适用于读操作频繁、写操作较少的场景。
  3. AbstractList:这是一个抽象类,实现了List接口的大部分方法,可以作为其他列表实现的基类。
  4. ImmutableList:这是Guava库中提供的一个不可变列表的实现。
  5. AbstractSequentialList:这是一个抽象类,实现了List接口,并且以链接结构存储元素,适用于顺序访问元素的场景。

除了这些,还有许多其他的列表实现,有些是第三方库提供的,有些是Java平台的其他部分提供的。每种列表实现都有其自身的特点和适用场景。

List的特点 (不重要)

  1. 有序性(Ordering):List中的元素按照插入顺序排列,每个元素都有一个与之关联的索引。
  2. 允许重复(Duplicates):List允许存储重复的元素,即同一个元素可以出现多次。
  3. 可变性(Mutability):大多数List实现是可变的,即可以动态地增加、删除和修改其中的元素。
  4. 索引访问(Indexed Access):可以通过索引来访问List中的元素,索引从0开始计数。
  5. Iterable接口(Iterable Interface):List实现了Iterable接口,因此可以使用增强型for循环和迭代器来遍历其中的元素。

如何创建List

以下是创建List的方法

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import java.util.Vector;

public class Main {	
    public static void main(String[] args) {
    	List<Integer> list_1 = new ArrayList<Integer>();
     	List<Integer> list_2 = new LinkedList<Integer>();
     	List<Integer> list_3 = new Vector<Integer>();
     	List<Integer> list_4 = new Stack<Integer>();
    }
}

那么你是否已经生出疑问?

List<Integer> list_1 = new ArrayList<Integer>();
ArrayList<Integer> list_5 = new ArrayList<Integer>();

这两句代码哪一种是你常写或者你常见的呢?

我要告诉你的是,这两句代码他们的效果是一样的,但为什么会有两种写法呢?如果你有意向深入,那么接下来的内容可以看看,你也可以选择先跳过


在语义上,这两句代码的效果是相同的,它们都创建了一个 ArrayList 类型的列表对象。

但是,在编程时,通常建议使用接口类型来声明变量,而不是具体的实现类。这样做有几个好处:

  1. 更好的封装:使用接口类型声明变量,可以将变量类型与实现类分离,从而提高代码的灵活性和可维护性。
  2. 降低耦合度:接口类型的变量只关心方法的签名,而不关心具体的实现细节,因此可以轻松地切换不同的实现类而不影响其他部分的代码。
  3. 符合面向接口编程的原则:面向接口编程是一种良好的编程实践,它能够提高代码的可复用性和可测试性。

因此,建议使用 List<Integer> list_1 = new ArrayList<Integer>(); 这种方式,以接口类型声明变量,从而获得更好的代码设计和可维护性。


List常用方法

  1. add(E e):在列表末尾添加指定的元素。
  2. get(int index):返回列表中指定位置的元素。
  3. set(int index, E element):将列表中指定位置的元素替换为指定的元素。
  4. remove(int index):移除列表中指定位置的元素,并返回被移除的元素。
  5. size():返回列表中的元素数。
  6. isEmpty():判断列表是否为空。
  7. contains(Object o):判断列表是否包含指定的元素。
  8. indexOf(Object o):返回列表中指定元素第一次出现的索引。
  9. lastIndexOf(Object o):返回列表中指定元素最后一次出现的索引。
  10. subList(int fromIndex, int toIndex):返回列表中指定范围的部分视图。

import java.util.ArrayList;

public class Main {	
    public static void main(String[] args) {
    	List<Integer> list_1 = new ArrayList<Integer>();
     	list_1.add(1);list_1.add(2);list_1.add(3);list_1.add(4);
     	list_1.set(0, 2);
     	list_1.get(0);
     	list_1.remove(0);
     	list_1.size();
     	list_1.isEmpty();
        list_1.contains(1);
        list_1.indexOf(2);
        list_1.lastIndexOf(2);
        list_1.subList(1, 3);
    }
}

当然,这些方法理论上对list的任意实现类都可以使用,但是它们并不包含输出效果,所以上面的代码没有任何输出。

值得一提的是,在上一篇Arrays类当中貌似忘记提了一点,就是list是可以直接使用System.out.println();语句输出的,和数组是不一样的。

import java.util.ArrayList;

public class Main {	
    public static void main(String[] args) {
    	List<Integer> list_1 = new ArrayList<Integer>();
     	list_1.add(1);list_1.add(2);list_1.add(3);list_1.add(4);
     	list_1.set(0, 2);
     	System.out.println(list_1.get(0));
     	list_1.remove(0);
     	System.out.println(list_1.size());
     	System.out.println(list_1.isEmpty());
     	System.out.println(list_1.contains(1));
     	System.out.println(list_1.indexOf(2));
     	System.out.println(list_1.lastIndexOf(2));
     	System.out.println(list_1.subList(1, 3));
        
    }
}

我们只要将它改成这样就会得到输出

2
3
false
false
0
0
[3, 4]

区别

要知道既然list分为了这么多不同的实现类,那么,一定是有区别滴!

ArrayList&LinkedList

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // 创建一个ArrayList
        ArrayList<String> arrayList = new ArrayList<>();

        // 添加元素到ArrayList
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add("Orange");

        // 遍历ArrayList
        for (String fruit : arrayList) {
            System.out.println(fruit);
        }
    }
}
import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        // 创建一个LinkedList
        LinkedList<String> linkedList = new LinkedList<>();

        // 添加元素到LinkedList的开头
        linkedList.addFirst("Apple");
        linkedList.addFirst("Banana");
        linkedList.addFirst("Orange");

        // 遍历LinkedList
        for (String fruit : linkedList) {
            System.out.println(fruit);
        }
    }
}

这两段代码看上去没有任何区别,但是 ArrayList支持随机访问

LinkedList 也支持随机访问,但是与 ArrayList 不同,LinkedList 的随机访问效率比较低,因为它需要从头或尾开始遍历链表,直到找到目标元素为止。这是因为 LinkedList 是通过链表实现的,每个节点只知道自己的前驱和后继节点,而不像 ArrayList 那样可以通过索引直接访问。

虽然 LinkedList 也支持随机访问,但是相对于 ArrayList 来说,它的性能会比较低,尤其是在大量随机访问操作时。因此,如果需要频繁进行随机访问操作,建议使用 ArrayList。

Vector

Vector 是 Java 中的一个传统集合类,它实现了动态数组的数据结构,与 ArrayList 类似。但是,Vector 是线程安全的,它的方法都是同步的,这意味着在多线程环境下,Vector 可以安全地被多个线程同时访问和修改。但是,这种同步机制也带来了性能上的一定损失。

以下是关于 Vector 类的一些重要点:

  1. 线程安全性Vector 的所有方法都是同步的,可以安全地在多个线程之间共享。
  2. 容量增长:与 ArrayList 类似,Vector 也具有自动扩容的功能,当容量不足时,会自动增加容量。
  3. 遍历和访问:可以使用类似于 ArrayList 的方式来访问和遍历 Vector 中的元素。
  4. 迭代器Vector 提供了迭代器用于遍历元素。
  5. 不推荐使用:由于其同步机制的性能损失以及更现代的 ArrayListLinkedList 类的出现,一般不推荐使用 Vector。相比之下,ArrayList 在非多线程环境下通常具有更好的性能,而 LinkedList 则在插入和删除操作频繁的情况下可能更高效。
    import java.util.Vector;
    
    public class Main {
        public static void main(String[] args) {
            Vector<Integer> vector = new Vector<>();
    
            // 添加元素
            vector.add(1);
            vector.add(2);
            vector.add(3);
    
            // 获取元素
            System.out.println("Element at index 1: " + vector.get(1));
    
            // 遍历元素
            System.out.print("Vector elements: ");
            for (Integer num : vector) {
                System.out.print(num + " ");
            }
            System.out.println();
        }
    }
    

    在实际开发中,除非需要线程安全的集合类,否则一般建议使用 ArrayListLinkedList 来替代 Vector


Stack

Stack 是 Java 中的一个类,它是一种后进先出(LIFO)的数据结构,类似于现实生活中的堆栈,例如书桌上的书堆或者餐厅里的盘子堆。在 Stack 中,最后添加的元素将会被最先移除。

我记得之前看过一个非常详细的例子但是忘记出处了,如果有人知道可以提醒我

就是说栈是否给你一种像栈道一样长长的感觉,那么就很容易联想到桶装的薯片,生产的时候最先装进去的那一片,就是你最后吃到的一片。

以下是关于 Stack 类的一些重要点:

  1. 继承关系Stack 类继承自 Vector 类,因此它包含了 Vector 类的所有方法,并在此基础上实现了堆栈的特性。
  2. 后进先出:在 Stack 中,最后添加的元素将会被最先移除,这就是后进先出的特性。
  3. 常用方法Stack 提供了一些常用的方法,例如 push() 将元素推入栈顶,pop() 将栈顶元素移除并返回,peek() 获取栈顶元素但不移除等。
  4. 空栈异常:在尝试对空栈执行 pop()peek() 操作时,将会抛出 EmptyStackException 异常。
  5. 应用场景Stack 在许多场景中都有应用,例如表达式求值、括号匹配、深度优先搜索等算法中。
import java.util.Stack;

public class Main {
    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();

        // 将元素推入栈顶
        stack.push(1);
        stack.push(2);
        stack.push(3);

        // 弹出栈顶元素
        int topElement = stack.pop();
        System.out.println("Top element popped: " + topElement);

        // 获取栈顶元素但不移除
        int peekElement = stack.peek();
        System.out.println("Top element (peek): " + peekElement);

        // 遍历栈元素
        System.out.print("Stack elements: ");
        while (!stack.isEmpty()) {
            System.out.print(stack.pop() + " ");
        }
        System.out.println();
    }
}

 当然我们上面也提到过,Stack在java中属于是Vector的分支,那么我们也讲了Vector在内存上的损耗。在实际开发中,Stack 类在一些特定场景下非常有用,例如在处理算法中的栈相关问题时。但需要注意的是,由于 Stack 是线程安全的类,使用 Deque 的实现类 ArrayDequeLinkedList 通常在性能上更好。

Deque(Double Ended Queue)在这里提一嘴,它也可以达成与栈一样的先进后出的效果。Deque 接口提供了一系列方法,可以从队列的两端添加、移除和检查元素。这使得 Deque 既可以作为队列使用,也可以作为栈使用,具有很高的灵活性。例如,addFirst()removeFirst() 方法用于操作队列的头部,addLast()removeLast() 方法用于操作队列的尾部。

import java.util.Deque;
import java.util.ArrayDeque;

public class Main {
    public static void main(String[] args) {
        // 创建一个双端队列
        Deque<Integer> deque = new ArrayDeque<>();

        // 添加元素到队列尾部
        deque.addLast(1);
        deque.addLast(2);
        deque.addLast(3);

        // 遍历队列,输出队列元素(先进先出)
        System.out.print("Queue (FIFO): ");
        while (!deque.isEmpty()) {
            System.out.print(deque.removeFirst() + " ");
        }
        System.out.println();

        // 添加元素到栈顶
        deque.addFirst(1);
        deque.addFirst(2);
        deque.addFirst(3);

        // 遍历栈,输出栈元素(后进先出)
        System.out.print("Stack (LIFO): ");
        while (!deque.isEmpty()) {
            System.out.print(deque.removeFirst() + " ");
        }
        System.out.println();
    }
}

与Arrays类的联系

这里算是个伏笔,也就是为什么我在上一篇介绍了Arrays类的相关内容,正是因为Arrays类的一些操作方法对List也是适用的。嘿嘿。

Integer[] array = {1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(array);
List<Integer> list = new ArrayList<>();
// 添加元素到列表
list.add(1);
list.add(2);
list.add(3);
// 转换为数组
Integer[] array = list.toArray(new Integer[list.size()]);
String[] array = {"apple", "banana", "orange"};
String str = Arrays.toString(array); // 输出:[apple, banana, orange]
List<String> list = Arrays.asList("apple", "banana", "orange");
int index = list.indexOf("banana"); // 返回 1
List<Integer> list = new ArrayList<>();
list.addAll(Arrays.asList(1, 2, 3)); // 添加元素 1, 2, 3
List<Integer> list = new ArrayList<>(Arrays.asList(5, 3, 1, 4, 2));
list.sort(Comparator.reverseOrder()); // 降序排序
List<String> list = Arrays.asList("apple", "banana", "orange");
boolean contains = list.contains("banana"); // 返回 true

以上都是一些例子


小细节

不知道你有没有生出疑问,反正我有

ArrayList<Integer> list = new ArrayList<>();
你不好奇最后面那个小括号是拿来干嘛的吗?老弄我心痒痒

ArrayList 的括号 "()" 内部可以包含一些参数,这些参数通常用于构造函数。在这里,你可以指定初始容量或者从另一个 Collection 对象初始化 ArrayList。

// 指定初始容量为 20 的 ArrayList
ArrayList<Integer> list = new ArrayList<>(20);

ArrayList<Integer> anotherCollection = new ArrayList<>();
// 添加一些元素到 anotherCollection 中

// 从另一个 Collection 对象初始化 ArrayList
ArrayList<Integer> list = new ArrayList<>(anotherCollection);

完结!!!

  • 44
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值