Java基礎教程——List(列表)


集合概述

Java中的集合,指一系列存儲數據的接口和類,可以解決復雜的數據存儲問題.
導包:import java.util.*;

簡化的集合框架圖如下:

List·列表

ArrayList

List是一個接口:

public interface List<E> extends Collection<E>{...}

ArrayList是最常用的一種List的子類(當然也實現了其他接口,也繼承了父類)。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{...}

ArrayList用法類似於數組,且其容量可按需要動態調整,亦被稱為動態數組。

數組最大的痛點是大小固定(可以改變,但是很麻煩)
ArrayList底層是用數組實現的,所以名字里帶了個數組(Array)。

示例:泰國旅行團
本示例展示了在List中使用泛型的必要性。
設定:泰國旅行團,約定只收Girl,有一個Boy混入,編譯沒問題,接機(輸出)時按Girl接收,會出錯。

import java.util.ArrayList;
import java.util.List;
public class TestArrayList {
	public static void main(String[] args) {
		// 開團
		List _泰國五日游 = new ArrayList();
		Girl _g1 = new Girl();
		Girl _g2 = new Girl();
		Girl _g3 = new Girl();
		_泰國五日游.add(_g1);
		_泰國五日游.add(_g2);
		_泰國五日游.add(_g3);
		// 混入
		Boy _b = new Boy();
		_泰國五日游.add(_b);
		System.out.println("...");
		// 接機
		for (int i = 0; i < _泰國五日游.size(); i++) {
			Girl g = (Girl) _泰國五日游.get(i);
		}
	}
}
class Boy {
}
class Girl {
}

代碼沒錯,運行出錯(對象本是Boy類型,偏要轉成Girl類型---類型轉換異常)

Exception in thread "main"

java.lang.ClassCastException

ArrayList<E>使用泛型

JDK 1.5之后,引入了泛型,可指定列表內元素的類型。類型不符合的元素不允許加入數組,這樣就能再編譯階段發現錯誤,避免運行時出錯的尷尬。

		// 開團
		List<Girl> _泰國五日游 = new ArrayList<Girl>();
		……
		// 混入
		Boy _b = new Boy();
		//提示代碼有錯誤:     _泰國五日游.add(_b);

遍歷

import java.util.*;
public class ListTraversal {
	public static void main(String[] args) {
		m010Traversal();
		m020線程安全版();
	}
	private static void m010Traversal() {
		System.out.println("=====遍歷");
		List<String> lst = new ArrayList<String>();
		lst.add("孫行者");
		lst.add("豬八戒");
		lst.add("沙悟凈");
		// (1)
		for (int i = 0; i < lst.size(); i++) {
			System.out.println("for遍歷集合:" + lst.get(i));
		}
		// (2)
		for (String s : lst) {
			System.out.println("foreach遍歷集合:" + s);
		}
		// (3)Iterator,迭代器。用於遍歷(迭代訪問)集合中的元素
		Iterator<String> it = lst.iterator();
		while (it.hasNext()) {
			System.out.println("Iterator遍歷:" + it.next());
		}
		// (4)Java 8:調用forEach()方法遍歷集合
		lst.forEach(s -> System.out.println("Lambda表達式遍歷集合:" + s));
	}
	// API文檔上說ArrayList不是同步的,即多線程環境下不安全
    // Collections.synchronizedList(...)將其轉為線程安全的列表
	private static void m020線程安全版() {
		System.out.println("=====線程安全版");
		List<String> lst = new ArrayList<String>();
		lst.add("孫行者");
		lst.add("豬八戒");
		lst.add("沙悟凈");
		// 解決線程安全問題
		List<String> synList = Collections.synchronizedList(lst);
		for (String s : synList) {
			System.out.println("foreach遍歷集合:" + s);
		}
	}
}

迭代器原理gif:https://www.cnblogs.com/tigerlion/p/10706386.html

更多方法

Collection相關方法

這些方法屬於Collection類,可以被子類繼承,因此通用性較強,不僅List能用,Set也能用。

返回類型 方法名稱 描述
boolean add(Object o) 添加元素
int size() 獲取元素個數
boolean contains(Object o) 判斷是否存在指定元素
boolean remove(Object o) 刪除元素
void clear() 清空
boolean isEmpty() 判空
Object[] toArray() 集合轉數組
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class TestCollection {
	public static void main(String[] args) {
		Collection<String> list = new ArrayList<>();
		for (int i = 0; i < 100; i++) {
			list.add("s" + i);
		}
		System.out.println("size():" + list.size());
		// 查找
		boolean contains = list.contains("s1");
		System.out.println("contains(Object o):" + contains);
		boolean empty = list.isEmpty();
		System.out.println("isEmpty():" + empty);
		// 集合轉數組
		Object[] array = list.toArray();
		System.out.println("toArray():" + Arrays.toString(array));
		// 刪除
		list.remove("s1");
		System.out.println("remove(Object o):" + list);
		list.clear();
		System.out.println("clear():" + list);
	}
}

List相關方法

List的派生類對象可以使用,Set不可用。
都是和索引相關的方法:

返回類型 方法名稱 描述
void add(int index, E element) 指定位置添加元素
int indexOf(Object o) 獲取指定元素的索引
E set(int index, E element) 替換指定位置的元素,返回更新前的元素
E get(int index) 獲取指定索引的元素
E remove(int index) 刪除指定索引的元素
import java.util.ArrayList;
import java.util.List;
public class TestList {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		for (int i = 0; i < 10; i++) {
			list.add("s" + i);
		}
		list.add(3, "舍衛國");
		int indexOf = list.indexOf("舍衛國");
		System.out.println("List.indexOf(Object o):" + indexOf);
		String set = list.set(0, "舍衛國趙長者");// 返回更新前的元素
		System.out.println("List.set(int index, E element):" + set);
		String get = list.get(0);
		System.out.println("List.get(int index):" + get);
		String remove = list.remove(3);// 返回被刪除的元素
		System.out.println("List.remove(int index):" + remove + list);
	}
}

源碼淺析:

ArrayList底層是通過數組實現,查詢快、增刪慢。API文檔上說ArrayList不是同步的,即多線程環境下不安全,但是效率高。

ArrayList每次擴容至1.5倍。

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // >>1:右移動1位=除以2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

Vector

和ArrayList用法一致。

元素超過它的初始大小 線程安全 效率
ArrayList *150% ×
Vector *200%

Vector是一個比較老的類,在JDK 1.0即已出現,不推薦使用(藍橋杯的練習題中出現過Vector,在那道題中只要知道它的用法和ArrayList一樣就行)。

雖然Vector是線程安全的,但是在線程安全方面也不推薦使用。推薦方案如下:

List<String> synList = Collections.synchronizedList(lst);

LinkedList

ArrayList使用數組實現,查詢快,增刪慢;
LinkedList使用鏈表實現,查詢慢,增刪快,適用於經常插入、刪除大量數據的場合,適合采用迭代器Iterator遍歷。
如果僅僅是在列表末尾插入數據,LinkedList的效率低於ArrayList,因為LinkedList調用add時需要創建對象,而ArrayList只是在容量不夠時才擴容。

LinkedList實現了List和Deque(雙端隊列)接口。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

特色方法(此時不能使用多態):

特色方法 解釋
addFirst() 頭部添加
addLast() 尾部添加
removeFirst() 頭部刪除
removeLast() 尾部刪除
push() 入棧,等效於addFirst()
pop() 出棧,等效於removeFirst()
offer() 入隊列,等效於addLast()
poll() 出隊列,等效於removeFirst()
import java.util.LinkedList;
public class TestLinkedList {
	public static void main(String[] args) {
		LinkedList<String> link = new LinkedList<String>();
		// addFirst:頭部添加數據
		link.addFirst("A");
		link.addFirst("B");
		link.addFirst("C");
		link.removeFirst();
		System.out.println(link);
		// addLast:尾部添加數據
		link.addLast("A");
		link.addLast("B");
		link.addLast("C");
		link.removeLast();
		System.out.println(link);
		link.clear();// 清空
		// push:將元素推入棧,等效於addFirst()
		link.push("A");
		link.push("B");
		link.push("C");
		// pop:出棧,調用的是removeFirst()
		link.pop();
		System.out.println("棧" + link);
		link.clear();// 清空
		// 將指定元素添加到此列表的末尾(最后一個元素)。
		// offer:入隊列:調用的是add方法,add又調用linkLast,和addLast一樣
		link.offer("A");
		link.offer("B");
		link.offer("C");
		// poll:出隊列:調用的是removeFirst()
		link.poll();
		System.out.println("隊列" + link);
	}
}

[B, A]
[B, A, A, B]
棧[B, A]
隊列[B, C]


*ArrayDeque·棧和隊列

  • 棧:先進后出
  • 隊列:先進先出

Deque(雙端隊列),是Queue的子接口,其實現類ArrayDeque和ArrayList的實現機制相似,使用Object[]數組存儲集合元素,當容量不足時,可以重新分配數組。

ArrayDeque可以當做棧和隊列使用。

import java.util.*;
public class TestArrayDeque {
	public static void main(String[] args) {
		m030棧();
		m040隊列();
	}
	static void m030棧() {
		System.out.println("=====棧");
		// push,pop(poll也可以)
		Deque<String> stack = new ArrayDeque<String>();
		stack.push("A");
		System.out.println(stack);// [A]
		stack.push("B");
		System.out.println(stack);// [B, A]
		stack.push("C");
		System.out.println(stack);// [C, B, A]
		System.out.println("peek()訪問第一個元素:" + stack.peek());// C
		System.out.println("pop()彈出:" + stack.pop());// C
		System.out.println(stack);// [B, A]
	}
	static void m040隊列() {
		System.out.println("=====隊列");
		// offrt,poll(pop也可以)
		Deque<String> queue = new ArrayDeque<String>();
		queue.offer("A");// [A]
		System.out.println(queue);
		queue.offer("B");// [A, B]
		System.out.println(queue);
		queue.offer("C");// [A, B, C]
		System.out.println(queue);
		System.out.println("peek()訪問第一個元素:" + queue.peek());// A
		System.out.println("poll()彈出:" + queue.poll());// A
		System.out.println(queue);// [B, C]
	}
}

運行結果:

=====棧
[A]
[B, A]
[C, B, A]
peek()訪問第一個元素:C
pop()彈出:C
[B, A]
=====隊列
[A]
[A, B]
[A, B, C]
peek()訪問第一個元素:A
poll()彈出:A
[B, C]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM