List列表
List是有序的集合(也称为序列)。通过List可以精确控制每个元素在列表中的插入位置。可以通过整数索引(在列表中的位置)访问元素,并在列表中搜索元素。与集合不同,列表通常允许重复的元素。简单地说,列表通常允许成对的元素e1和e2,例如e1.equals(e2),如果它们允许空元素,则通常允许多个空元素。
一、List
/*
* List元素特性:有序(添加顺序)可重复
* 存入顺序和去除顺序一致,有指定下标可以表示数据的唯一性,故可存在重复元素
* 底层实现
* ArrayList: 底层是数组,查询效率高,添加和删除效率低,是VectorList升级版
* VectorList过时了,其属于线程安全;ArrayList其属于非线程安全,所以ArrayList效率较高
* LinkedList: 底层是双向链表,查询效率较低,添加和删除效率较高
* */
public class _01_Collection_List {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
list.add(2);
list.add(3);
list.add(null);
list.remove(null);
// 输出引用的时候,自动调用该对象的toString方法,ArrayList覆写toString
System.out.println(list);
}
}
二、ArrayList
/*
* ArrayList:
* 底层索引数组,下标0开始
* 初始化容量为10,满后扩大容量为原始容量 1.5倍。
* 非线程安全,效率高
* Vector:
* 初始化容量为10,满后扩大2倍,线程安全,效率低。已废弃
* */
public class _02_Collection_ArrayList {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 测试默认容量和扩大容量
ArrayList<Object> arrayList = new ArrayList<>();
// 添加10个对象
for (int i = 0; i < 10; i++) {
arrayList.add(2);
}
System.out.println(arrayList.size());
// 添加第11个对象
arrayList.add(2);
// -------------
// 获得arrayList类
Class cls = arrayList.getClass();
// 返回一个Field对象,该对象反映由该类对象表示的类或接口的指定声明字段。
// name参数是一个字符串,用于指定所需字段的简单名称。
Field declaredFields = cls.getDeclaredField("elementData");
// 设置访问权限
declaredFields.setAccessible(true);
// -------------
// ArrayList对象中的底层数组
Object[] elementData = (Object[]) (declaredFields.get(arrayList));
// 数组长度为15
System.out.println(elementData.length);
// 保存有11个对象
System.out.println(arrayList.size());
// 自实现List一些方法
System.out.println("------------");
A a = new A();
List_Test aaa = new List_Test("AAA", 18);
List_Test bbb = new List_Test("BBB", 19);
List_Test ccc = new List_Test("CCC", 20);
a.add(aaa);
a.add(bbb);
a.add(ccc);
a.add(1);
a.add(2);
System.out.println(a.size());
a.remove(bbb);
Object[] objects = a.toArray();
for (Object object : objects) {
System.out.println(object);
}
System.out.println("----------");
System.out.println(a.size());
}
}
// 自实现ArrayList一些方法
class A{
private Object[] elementData;
private int size = 0;
public A() {
elementData = new Object[10];
}
// 增
public void add(Object o){
elementData[size] = o;
size++;
}
// 删
public void remove(Object o){
Boolean flag = false;
int index = 0;
for (int i = 0; i < elementData.length; i++) {
if (elementData[i]!=null){
if (elementData[i].equals(o)){
flag = true;
index = i;
}
}
}
if (flag){
for (int i = index; i < elementData.length-1; i++) {
elementData[i] = elementData[i+1];
}
elementData[--size] = null;
}
}
// 大小
public int size(){
return size;
}
// 转引用数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
}
class List_Test{
private String name;
private int age;
public List_Test(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (obj instanceof List_Test){
List_Test other = (List_Test) obj;
return other.getName().equals(this.name) && other.getAge() == other.age;
}
return false;
}
@Override
public String toString() {
return "name: " + this.name + ", age: " + this.age;
}
}
三、ArrayList常用方法
/*
* list遍历方式
* while、for、forEach、iterator
* 数组中:只能保存同一数据类型的元素
* 集合中:可以保存任意类型
* ArrayList底层是数组,是Object[]数组,若单独声明Object[]数组,同理也能存储任意数据
* 任意数据都可以转型为Object,包括基本数据类型
* 常用方法:
* add(E e): 将指定元素添加到尾部
* add(int index,E e): 在指定位置 插入元素,该位置上的元素统一向后移动
* set(int index,E e): 更改指定位置是元素
* get(int index): 获取索引上元素
* remove(int index): 删除指定索引上元素
* remove(Object o): 删除指定元素
* */
public class _03_Collection_ArrayList {
public static void main(String[] args) {
List<Object> arrayList = new ArrayList<>();
// 把数据添加到尾部
arrayList.add(11);
// 在指定位置 插入元素,该位置上的元素统一向后移动
arrayList.add(0,12); // 动态绑定,根据参数类型确定使用方法(方法重载情况)
System.out.println(arrayList);
// 获取指定下标对应数据
Object obj = arrayList.get(1);
System.out.println(obj);
// 删除指定下标(下标为3)元素
arrayList.add(3);
arrayList.add(4);
System.out.println(arrayList);
arrayList.remove(3); // 删除指定index
System.out.println(arrayList);
// 删除元素值为Integer 且元素值为3
arrayList.add(4);
arrayList.remove(new Integer(3)); // value
System.out.println(arrayList);
System.out.println("----------");
// 遍历
for (Object o : arrayList) {
System.out.println(o);
}
System.out.println("----------");
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
}
}
四、LinkedList
/*
* LinkedList:
* 底层是双向链表,链表中保存的是节点,每个节点有三个元素
* 三个元素:1 自身对象 2 上一个节点地址 3 下一个节点地址
* 链表没有下标,内存空间不连续,故查询较慢
* 内存空间中保存了上下节点的引用,故添加删除操作较快
* 常用方法:
* add(E e): 将元素添加到尾部,成功返回true
* push(E e): 将元素添加到头部,成功返回true
* addFirst(E e): 将元素添加到头部
* addLast(E e): 将元素添加到尾部
* offerFirst(E e): 将元素添加到头部,成功返回true
* offerLast(E e): 将元素添加到尾部,成功返回true
* get(int index): 返回指定下标对应数据(由于没有下标,因此是模拟下标,方便查询使用)
* 本质上调用了两个方法:LinkLast 和 LinkFirst
* */
public class _04_Collection_LinkedList {
public static void main(String[] args) {
LinkedList<Object> linkedList = new LinkedList<>();
linkedList.add(1);
linkedList.push(2);
linkedList.addFirst(3);
linkedList.addLast(4);
linkedList.offerFirst(5);
linkedList.offerLast(6);
// 遍历和ArrayList一致
for (Object o : linkedList) {
System.out.println(o);
}
System.out.println("----------");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
System.out.println("----------");
LinkedList<Object> linked = new LinkedList<>();
linked.add("a"); // 添加第一个元素
linked.add("b");
linked.add("c");
linked.push("d");
System.out.println(linked.size());
linked.remove("a");
System.out.println(linked.get(2));
Iterator<Object> iterator = linkedList.iterator();
linkedList.isEmpty();
}
}
// 手动实现一个链表?
/*
// 已添加元素
transient int size = 0;
// 首节点
transient LinkedList.Node<E> first;
// 尾节点
transient LinkedList.Node<E> last;
// 节点类,LinkedList中的静态内部类
// 保存共三个属性,1、添加的数据item 2、上一个节点内存地址prev 3、下一个节点内存地址next
// LinkedListz中,保存了首尾两个节点类对象。方便使用,可以首尾添加元素
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;
}
}
* */
/*
// 元素添加到尾部,底层实现(添加到首部类似)
public boolean add(E e) {
linkLast(e);
return true;
}
public void addLast(E e) {
linkLast(e);
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
// 核心方法, Links e as last element.
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null); // new一个节点对象
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++; // 计数
}
// 节点类(内部类)
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;
}
}
* */
/*
// LinkList中get方法
public E get(int index) {
checkElementIndex(index); // 校验下标
return node(index).item; // 获取数据
}
// 检查index
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
// 获取节点数据,get模拟下标方式,本质还是遍历,从头开始找,在开始做了判断
// 如果index位于左半部分,正序遍历,从first开始;位于右半部分,逆序遍历,从last开始
// 本质是循环通过next和prev去找到索引值。实现方式和ArrayList中的get方法完全不同
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) { // 正序遍历,(size >> 1)表示size值得二进制数右移一位,表示除2(size=8,size >> 1 = 4)
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next; // 一直循环到index的前一位,返回x.next
return x;
} else { // 逆序遍历
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev; // 一直循环到index的后一位,返回x.prev
return x;
}
}
ArrayList中get方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
* */
/*
// LinkList中remove方法
// 循环找到要删除的节点,比如a; 然后执行a.prev.next = a.next a.next.prev = a.prev
public boolean remove(Object o) { // 查找节点
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) { // equals方法判断
unlink(x);
return true;
}
}
}
return false;
}
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; // 赋值为null,相当于删除,null后等待GC回收
}
// 判断要删除节点的下一个是否为空,如果为空,则此节点为尾节点
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
* */
总结
这里简单介绍了集合中的List列表,它有两个实现类:ArrayList和LinkedList,底层实现分别数组和双向链表。ArrayList查询效率高,添加、删除效率低,LinkedList查询效率低,添加、删除效率高。