• 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]
    
  • 相关阅读:
    Linux编程之epoll
    Linux IO模式及 select、poll、epoll详解
    与程序员相关的CPU缓存知识
    JDK源码阅读-FileOutputStream
    JDK源码阅读-FileInputStream
    JDK源码阅读-ByteBuffer
    Java如何保证文件落盘?
    Linux/UNIX编程如何保证文件落盘
    JDK源码阅读-RandomAccessFile
    JDK源码阅读-FileDescriptor
  • 原文地址:https://www.cnblogs.com/tigerlion/p/11179202.html
Copyright © 2020-2023  润新知