20集合(List接口补充)
Posted shawnyue-08
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20集合(List接口补充)相关的知识,希望对你有一定的参考价值。
List接口
List接口:元素有序,可重复,集合中每个元素都有其对应的索引。
常用实现类有:ArrayList、LinkedList、Vector。
ArrayList源码
JDK 1.7情况下:
ArrayList<String> list = new ArrayList<>();
使用空参构造器创建集合,底层创建了长度为10的Object类型数组elementData。
扩容机制:
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
//扩容为原来的1.5倍
elementData = Arrays.copyOf(elementData, newCapacity);
Arrays数组工具类的copyOf()拷贝原数组,底层会根据newCapacity创建一个新的数组,然后
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
这样,返回的copy数组是一个大数组,其余元素均为0。
JDK 1.8情况下:
通过空参构造器创建集合
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
通过有参构造器创建集合
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
transient Object[] elementData;
private static final Object[] EMPTY_ELEMENTDATA = {};
当使用add()方法向空参创建的集合中添加元素时:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
首先确保内部容量,ensureCapacityInternal(1),先看内部的容量是否大于1,
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
首先计算容量,
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
if的条件表达式返回true,return Math.max(10, 1);所以返回10。
private static final int DEFAULT_CAPACITY = 10;
计算完容量后,执行ensureExplicitCapacity(10)方法;
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
if的条件表达式返回true(minCapacity = 10),执行扩容。
扩容机制和JDK 1.7一致。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//0
int newCapacity = oldCapacity + (oldCapacity >> 1);
//0
if (newCapacity - minCapacity < 0)
//0 - 10 < 0
newCapacity = minCapacity;
//newCapacity = 10
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);
//elementData = Arrays.copyOf(elementData, 10);
}
方法弹栈,继续执行add(E e)方法。
elementData[size++] = e;
return true;
elementData[0] = e,size = 1;
结论:JDK 1.7是饿汉式创建elementData,而JDK 1.8是懒汉式创建elementData,在第一次add添加时再扩容。
并发修改异常
Java容器有一种保护机制,能够防止多个进程同时修改同一个容器的内容。如果你在迭代遍历某个容器的过程中,另一个进程介入其中,并且插入,删除或修改此容器的某个对象,就会立刻抛出ConcurrentModificationException
。
使用迭代器删除集合元素
package org.westos.demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
/**
* @author lwj
* @date 2020/5/12 14:23
*/
public class MyTest {
public static void main(String[] args) {
ArrayList<String> strings = new ArrayList<>();
strings.add("aaa");
strings.add("bbb");
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
iterator.remove();
}
//删除集合中的元素
//增强for不能在遍历途中修改集合,要想在遍历过程中修改集合,只能使用迭代器或者普通for循环
System.out.println(strings);
//[]
}
}
LinkedList源码
LinkedList,泛型类,继承泛型抽象类,泛型接口,暂不声明类型,等到创建对象时声明类型。
LinkedList<String> strings = new LinkedList<>();
将参数"String"传递给E类型,即Node节点的值域存储字符串类型。当传递自定义类型时,
LinkedList<Student> students = new LinkedList<>();
Node节点的值域就存储Student类型数据。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
transient int size = 0;
transient Node<E> first;
//第一个节点node的指针
transient Node<E> last;
//最后一个节点的指针
}
Node是LinkedList的静态内部泛型类,和LinkedList共用一个类型,定义了一个节点类,包含item域,next后驱节点,prev前驱结点。
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
当使用add(E e)方法向链表中增加元素时,
public boolean add(E e) {
linkLast(e);
return true;
}
//linkLast(e) 链接e作为最后一个元素
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
//prev = l, item = e, next = null
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
方法addLast(E e)和add(E e)逻辑相同,但是返回值为void;
public void addLast(E e) {
linkLast(e);
}
方法addFirst(E e)底层调用linkFirst(E e);
public void addFirst(E e) {
linkFirst(e);
}
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
方法add(int index, E e)底层:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
插入节点,首先调用node(index)找到待插入位置的节点。
删除节点
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
package org.westos.demo;
import java.util.LinkedList;
/**
* @author lwj
* @date 2020/5/12 15:44
*/
public class MyTest2 {
public static void main(String[] args) {
LinkedList<String> strings = new LinkedList<>();
strings.add("aaa");
strings.addLast("bbb");
strings.addFirst("ccc");
System.out.println(strings);
//[ccc, aaa, bbb]
strings.add(1, "ddd");
System.out.println(strings);
//[ccc, ddd, aaa, bbb]
String remove = strings.remove(3);
System.out.println(remove);
//bbb
System.out.println(strings);
//[ccc, ddd, aaa]
}
}
List接口的常用方法
LinkedList,双向链表,内部没有声明数组(不是数组实现),而是定义了Node<E>类型的first和last,用于记录首尾元素。同时,定义内部类Node<E>,作为LinkedList中保存数据的基本结构。
List接口除了从Collection接口继承的方法外,List接口添加了一些根据索引来操作集合元素的方法。
方法名 | 描述 |
---|---|
void add(int index, E element) | 将指定元素插入此列表的指定位置 |
void addAll(int index, Collection<? extends E> c) | 将指定集合插入此列表的指定位置 |
E get(int index) | 返回此列表中指定位置的元素 |
int indexOf(Object o) | 返回此列表中指定元素第一次出现的索引 |
int lastIndexOf(Object o) | 返回此列表中指定元素最后一个出现的索引 |
E remove(int index) | 删除此列表中指定位置的元素 |
E set(int index , E element) | 用指定的元素替换此列表中指定位置的元素 |
List<E> subList(int fromIndex, int toIndex) | 返回此列表中指定的fromIndex到toIndex的元素集合 |
package org.westos.demo;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/**
* @author lwj
* @date 2020/5/12 18:56
*/
public class MyTest3 {
public static void main(String[] args) {
LinkedList<String> strings = new LinkedList<>();
strings.add(0, "aaa");
strings.add(1, "bbb");
strings.add(2, "ccc");
strings.addFirst("AAA");
strings.addLast("CCC");
System.out.println(strings);
//[AAA, aaa, bbb, ccc, CCC]
strings.addAll(5, Arrays.asList("ddd", "DDD"));
System.out.println(strings);
//[AAA, aaa, bbb, ccc, CCC, ddd, DDD]
String remove = strings.remove(0);
//删除指定索引的元素
System.out.println(remove);
//AAA
System.out.println(strings);
//[aaa, bbb, ccc, CCC, ddd, DDD]
//strings.removeFirst();
//strings.removeLast();
String s = strings.get(0);
//根据索引返回元素
System.out.println(s);
//aaa
String first = strings.getFirst();
System.out.println(first);
//aaa
String last = strings.getLast();
System.out.println(last);
//DDD
int bbb = strings.indexOf("bbb");
System.out.println(bbb);
//1
int ccc = strings.lastIndexOf("ccc");
System.out.println(ccc);
//2
String aaa = strings.set(0, "AAA");
System.out.println(aaa);
//aaa
System.out.println(strings);
//[AAA, bbb, ccc, CCC, ddd, DDD]
List<String> strings1 = strings.subList(0, 3);
System.out.println(strings1);
//[AAA, bbb, ccc]
System.out.println(strings);
//[AAA, bbb, ccc, CCC, ddd, DDD]
}
}
总结:
增:add(Object obj)/add(int index, E e)/addFirst(E e)/addLast(E e)/addAll(Collection c)/addAll(int index, Collection c)
删:remove(Object obj)/remove(int index)/removeFirst()/removeLast()
改:set(int index, E element)
查:get(int index)/getFirst()/getLast()
List的遍历方法
除了普通for循环+size()方法、增强for循环、Iterator接口外,新增了一个ListIterator接口。
package org.westos.demo;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
/**
* @author lwj
* @date 2020/5/12 19:30
*/
public class MyTest4 {
public static void main(String[] args) {
LinkedList<String> strings = new LinkedList<>(Arrays.asList("aaa", "bbb", "ccc"));
//1、普通for循环 + size()
for (int i = 0; i < strings.size(); i++) {
System.out.println(strings.get(i));
}
//2、增强for循环
for (String string : strings) {
System.out.println(string);
}
//3、Iterator
Iterator<String> iterator = strings.iterator();
/*
public Iterator<E> iterator() {
return listIterator();
}
*/
System.out.println(iterator);
//java.util.LinkedList$ListItr@1b6d3586
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
//4、ListIterator
ListIterator<String> stringListIterator = strings.listIterator();
System.out.println(stringListIterator);
//java.util.LinkedList$ListItr@4554617c
//private class ListItr implements ListIterator<E> {}
while (stringListIterator.hasNext()) {
System.out.println(stringListIterator.next());
}
//5、ListIterator从后向前遍历
ListIterator<String> stringListIterator1 = strings.listIterator(strings.size());
while (stringListIterator1.hasPrevious()) {
System.out.println(stringListIterator1.previous());
}
}
}
练习题
remove(int index)和remove(Object o)
package org.westos.demo;
import java.util.Arrays;
import java.util.LinkedList;
/**
* @author lwj
* @date 2020/5/12 19:42
*/
public class MyTest5 {
public static void main(String[] args) {
LinkedList<Integer> integers = new LinkedList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
Integer remove = integers.remove(2);
//删除集合中索引为2的元素
System.out.println(remove);
//3
//根据索引删除,返回原索引位置的值
//如果想要删除值为2的元素呢
boolean remove1 = integers.remove(Integer.valueOf(2));
System.out.println(remove1);
//true
//删除值,返回boolean类型
System.out.println(integers);
//[1, 4, 5, 6]
}
}
以上是关于20集合(List接口补充)的主要内容,如果未能解决你的问题,请参考以下文章
Java的Iterator迭代器补充,增强for循环,泛型,List接口,set接口