Java集合中List

本文详细介绍了Java集合框架中的List接口及其两种主要实现类ArrayList和LinkedList。ArrayList基于数组实现,查询快但增删慢,适合频繁读取场景;LinkedList采用双向链表,查询慢但增删快,适合频繁插入删除操作。文章还涵盖了这两个类的常用方法及其实现原理。
摘要由CSDN通过智能技术生成

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查询效率低,添加、删除效率高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值