xiguayaaaaa
文章28
标签4
分类7
集合框架List笔记

集合框架List笔记

集合框架List笔记

Java集合

Java集合分为List、Set、Map和Queue。

List代表有序,可重复的集合。

Set代表无序,不可重复的集合。

Map代表具有映射关系的集合。

ArrayList

ArrayList是List的子类,底层就是用数组保存数据以及对数据进行操作。

常用构造方法

  • public ArrayList():无参构造,默认创建一个空数组,当添加第一个数据时,需要扩容,默认长度为10,当添加第十一个数据时,再次扩容,长度为原长度的1.5倍,以此类推。
  • public ArrayList(int initialCapacity):默认的长度为传入的参数,超出长度添加时,需要扩容,长度为原长度的1.5倍。

ArrayList的长度最小为0,最大为Integer的最大值(2^31-1)。

常用API

方法 描述
add(Object o) 添加数据
add(int index,Object o) 在制定索引处添加元素
size() 获取元素个数
get(int index) 获取索引处的元素
isEmpty() 判断集合是否为空
indexOf(Object o) 判断某个元素第一次出现的位置
E remove(int index) 移除索引处元素,并返回该元素
boolean remove(Object o) 移除元素
clear() 清空元素
set(int index ,E e) 修改索引处的元素
iterator() 获取迭代器
toArray() 将集合转换为数组

手动实现ArrayList

public class Ary {
    
    int[] ary;
    int num;//数组内的内容的个数
    
    
    
    public Ary() {//初始化
        this.ary = new int[10];
        this.num = 0;
    }

    

    @Override
    public String toString() {
        return Arrays.toString(ary);
    }



    public void add(int e) {
        if(num<10) {//如果数组中的内容小于10
            //添加元素值
            ary[num]=e;
            //添加一个,内容加一
            num++;
        }else {//数组内容等于10
            System.out.println("数组已满");
        }
    }
    
    public void add(int index,int e) {
        if(num<10) {
            if(index>=0 && index<=num-1) {//索引只能大于等于0,小于等于数组中内容数减一
                for(int i=num;i>index;i--) {//先将包括索引位置的值向后移一位
                    ary[i]=ary[i-1];
                }
                ary[index]=e;//将索引位置的值设置为传入的e
            }else {
                System.out.println("索引错误");
            }
        }else {
            System.out.println("数组已满");
        }
    }
    
    public void remove(int index) {
        if(num<10) {
            if(index>=0 && index<=num-1) {
                for(int i=index;i<num;i++) {//索引位置开始,通过赋值将元素值向前移动一位
                    ary[i]=ary[i+1];
                }
                num--;//数组内容数减一
            }else {
                System.out.println("索引错误");
            }
        }else {
            System.out.println("数组已满");
        }
    }
    
    public int get(int index) {
        int get=0;
        if(index>=0 && index<=num-1) {
            get=ary[index];//获取索引处的元素值
            return get;
        }else {
            System.out.println("索引错误");
            return -1;
        }
    }
    
    public void set(int index,int e){
        if(index>=0 && index<=num-1) {
            ary[index]=e;//将索引处内容重新赋值
        }else {
            System.out.println("索引错误");
        }
    }
    
    
    
    
    
    public static void main(String[] args) {
        Ary ary=new Ary();
        ary.add(1);
        ary.add(6);
        ary.add(8);
        ary.add(2);
        ary.add(4);
//        System.out.println(ary.toString());
//        ary.add(2, 7);
//        System.out.println(ary.toString());
//        ary.remove(3);
//        ary.set(0, 9);
        System.out.println(ary.toString());
//        System.out.println("获取的元素值为"+ary.get(2));
    }
}

ArrayList和Vector

Vector与ArrayList基本类似

  • Vector的扩容为原长度的2倍,ArrayList则是原长度的1.5倍。
  • Vector是线程安全的。
  • Vector调用无参构造时,直接创建初始化长度为10的数组,而ArrayList调用无参构造是创建一个长度为0的空数组,添加第一个元素时,数组长度扩容为10。

LinkedList

手动实现LinkedList链表

单链表

/**
 * 单链表的增删查改实现
 *
 * @author xigua
 * @date 2022/8/116:51
 */
public class MyLinkedList {
    /** 内部类 */
    private class Node{
        /** 节点中存的数据 */
        public Object date;

        /** 下一个节点的引用地址  */
        public Node next;

        public Node(Object date, Node next) {
            this.date = date;
            this.next = next;
        }
    }

    /** 首节点 */
    private Node first;
    /** 尾节点 */
    private Node last;
    /** 元素个数 */
    private int size=0;
    /** 初始化链表*/
    public MyLinkedList() {
        first=null;
        last=null;
    }
    /** 判断索引是否越界*/
    public void checkIndex(int index){
        if(index<0 || index>size-1){
            throw new IndexOutOfBoundsException("索引越界");
        }
    }

    /** 获取存储的节点个数*/
    public int size(){
        return size;
    }

    /** 获取索引处对象存放的数据*/
    public Object get(int index){
        return getNode(index).date;
    }
    /** 获取索引处的节点*/
    public Node getNode(int index){
        checkIndex(index);
        Node current=first;
        for (int i = 0; i < index; i++) {
            current=current.next;
        }
        return current;
    }

    /** 末尾添加元素*/
    public boolean add(Object o){
        /** 链表为空*/
        if (size==0){
            /** 只有一个节点,首节点,尾节点都是它*/
            Node node=new Node(o,null);
            first=node;
            last=node;
        }else {
            /** 链表不为空*/
            Node node=new Node(o,null);
            last.next=node;
            last=node;
        }
        size++;
        return true;
    }

    /** index处添加元素*/
    public boolean add(int index,Object o){
        checkIndex(index);
        /** 末尾添加*/
        if (index==size-1){
            return add(o);
        }
        /** 头部添加*/
        else if (index==0){
            Node node=new Node(o,first);
            first=node;
        }
        /** 中间添加*/
        /**
         * 获取索引处的节点
         * 和索引前的节点
         * 创建新节点
         * */
        else {
            Node indexNode=getNode(index);
            Node preNode=getNode(index-1);
            Node node=new Node(o,indexNode);
            preNode.next=node;
        }
        size++;
        return true;
    }

    /** 删除索引处的节点*/
    public Object remove(int index){
        checkIndex(index);
        Object o=null;
        /** 只有一个节点时
         * 只有索引处一个节点
         * 节点赋空值
         * */
        if (size==1){
            Node node=getNode(index);
            node.date=null;
            first=null;
            last=null;
        }
        /**
         * 删除首节点
         * */
        else if (index==0){
            Node firstNode=getNode(index);
            first=firstNode.next;
            o=firstNode.date;
            firstNode.next=null;
            firstNode.date=null;
        }
        /** 删除尾节点
         *  倒数第二个节点的next赋null
         * */
        else if(index==size-1){
            Node lastNode=getNode(index);
            o=lastNode.date;
            last=getNode(index-1);
            getNode(index-1).next=null;
        }
        /** 删除中间节点
         *  获取要删除的节点,以及它的前节点和后节点
         *  前节点的next指向后节点
         *  删除的节点next赋null
         * */
        else{
            Node indexNode=getNode(index);
            o= indexNode.date;
            Node preNode=getNode(index-1);
            Node nextNode=indexNode.next;
            preNode.next=nextNode;
            indexNode.next=null;
        }
        size--;
        return o;
    }

    /** 修改节点的值
     *  获取要修改的节点
     *  修改节点的date
     * */
    public Object set(int index,Object o){
        checkIndex(index);
        Node node=getNode(index);
        Object oldValue=node.date;
        node.date=o;
        return oldValue;
    }

}

双链表

/**
 *双向链表的增删查改实现
 *
 * @author xigua
 * @date 2022/8/119:38
 */
public class TwoLinkedList {
    /**
     * 内部类
     * */
    private class Node{
        /** 储存的数据*/
        private Object date;
        /** 前节点的地址*/
        private Node previous;
        /** 后节点的地址*/
        private Node next;
        /** 每个节点都包含的三个属性*/
        public Node(Object date, Node previous, Node next) {
            this.date = date;
            this.previous = previous;
            this.next = next;
        }
    }

    /** 首节点*/
    private Node first;
    /** 尾节点*/
    private Node last;
    /** 节点数*/
    private int size=0;

    public TwoLinkedList() {
        this.first = null;
        this.last = null;
    }

    /** 判断索引是否越界*/
    public void checkIndex(int index){
        if (index<0 || index>size-1){
            throw new IndexOutOfBoundsException("索引越界!");
        }
    }
    /** 获取节点个数*/
    public int size(){
        return size;
    }
    /** 获取索引处节点
     *  第n个节点,就是首节点next n次
     * */
    public Node getNode(int index){
        checkIndex(index);
        Node current=first;
        for (int i = 0; i < index; i++) {
            current=current.next;
        }
        return current;
    }
    /**
     * 获取节点中存的数据
     * */
    public Object get(int index){
        return getNode(index).date;
    }
    /** 末尾处添加节点*/
    public boolean add(Object o){
        /** 链表为空时
         *  只有添加的这一个节点,无前后节点
         * */
        if (size==0){
            Node node=new Node(o,null,null);
            first=node;
            last=node;
        }else {
            /** 链表不为空,创建新节点,last的next指向它,它的previous指向last,最后last变成新加的节点*/
            Node node=new Node(o,last,null);
            last.next=node;
            last=node;
        }
        size++;
        return true;
    }
    /** 索引处添加节点*/
    public boolean add(int index,Object o){
        checkIndex(index);
        /** 在首节点前添加
         *  创建需要添加的节点
         *  新节点的previous为null,next指向first
         *  first的previous指向新节点,first变为新节点
         * */
        if(index==0){
            Node node=new Node(o,null,first);
            first.previous=node;
            first=node;
        }
        /** 在尾节点处添加
         *  调用add(Object o)方法
         * */
        else if (index==size-1){
            return add(o);
        }
        /** 在中间添加
         *  创建新节点,找出索引处的节点和该节点的前节点
         *  前节点的的next指向新节点,新节点的previous指向前节点,next指向索引节点,索引节点的previous指向新节点
         * */
        else{
            Node indexNode=getNode(index);
            Node preNode=getNode(index-1);
            Node node=new Node(o,preNode,indexNode);
            preNode.next=node;
            indexNode.previous=node;
        }
        size++;
        return true;
    }

    /** 删除索引处的节点*/
    public Object remove(int index){
        checkIndex(index);
        Object o=null;
        /** 只有一个节点值*/
        if (size==1){
            Node node=getNode(index);
            node.date=null;
            first=null;
            last=null;
        }
        /** 删除首节点
         *  first变为index+1的节点
         *  index的节点的next赋空
         *  index+1的节点的previous赋空
         * */
        else if (index==0){
            Node indexNode=getNode(index);
            Node nextNode=getNode(index+1);
            nextNode.previous=null;
            indexNode.next=null;
            o=indexNode.date;
            indexNode.date=null;
            first=nextNode;
        }
        /** 删除尾节点
         *  index-1的节点的next赋空
         *  index节点的previous赋空
         *  last变为index-1的节点
         *  index节点的值赋空
         * */
        else if(index==size-1){
            Node indexNode=getNode(index);
            Node preNode=getNode(index-1);
            preNode.next=null;
            indexNode.previous=null;
            o=indexNode.date;
            indexNode.date=null;
            last=preNode;
        }
        /** 删除中间节点
         *  获取要删除的节点,以及它的前节点后节点
         *  前节点的next指向后节点
         *  后节点的previous指向前节点
         *  索引节点的previous和next以及data赋空
         * */
        else{
            Node indexNode=getNode(index);
            Node preNode=getNode(index-1);
            Node nextNode=getNode(index+1);
            preNode.next=nextNode;
            nextNode.previous=preNode;
            o=indexNode.date;
            indexNode.previous=null;
            indexNode.next=null;
            indexNode.date=null;
        }
        size--;
        return o;
    }

    /** 修改索引处的值
     *  获取索引的节点
     *  修改节点的值
     * */
    public void set(int index,Object o){
        Node node=getNode(index);
        node.date=o;
    }
}

测试

public class Test {
    public static void main(String[] args) {
        MyLinkedList list=new MyLinkedList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(1,66);
        list.remove(0);
        list.set(2,8);
        for (int i = 0; i < list.size() ; i++) {
            System.out.println(list.get(i));
        }

//        TwoLinkedList list=new TwoLinkedList();
//        list.add(1);
//        list.add(2);
//        list.add(3);
////        list.add(2,8);
////        list.remove(1);
//        list.set(2,8);
//        for (int i = 0; i < list.size(); i++) {
//            System.out.println(list.get(i));
//        }
    }
}

ArrayList和LinkedList的区别

ArrayList和LinkedList虽然都是List接口的子类,但是在底层实现以及效率上存在以下区别

  1. ArrayList和LinkedList都实现了List接口
  2. ArrayList和LinkedList都是非线程安全的,因此在多线程环境下可能会出现出现不同步的情况
  3. ArrayList底层实现是数组,LinkedList底层实现是双向链表
  4. ArrayList因为底层实现是数组,并且支持随机访问因此查找效率高,但是ArrayList在新增元素时会扩容以及复制数组元素,并且删除时也会进行数组复制,所以增删效率低。而LinkedList不支持随机访问,获取元素时必须从首节点开始从前往后遍历查找,因此查找效率低。但是增加和删除时最多涉及到两个节点的操作,因此增删效率高

ArrayList和LinkedList的遍历方式

        /**
         * for循环遍历
         * */
        System.out.println("for循环遍历:");
        for (int i = 0;i < list.size();i++){
            System.out.println(list.get(i));
        }

        /**
         * foreach循环遍历
         * */
        System.out.println("foreach循环遍历:");
        for (String s : list) {
            System.out.println(s);
        }

        /**
         * 迭代器遍历
         *
         * 相当于指针从第一个前开始(空值),通过hasNext判断
         * 当前指向的后面是否有值,有为true,通过next()输出
         * 该值。
         *
         * 不能连续两次使用同一个while遍历,因为第一次遍历完后
         * 指针在最后一位上,无法向后遍历
         * */
        System.out.println("迭代器遍历:");
        Iterator<String> iterator=list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

        /**
         * List迭代器遍历
         * */
        System.out.println("List迭代器遍历:");

        /**
         * 从前往后遍历
         * */
        ListIterator<String> listIterator=list.listIterator();
        while(listIterator.hasNext()){
            System.out.println(listIterator.next());
        }

        /**
         * 从后往前遍历
         * */
        while(listIterator.hasPrevious()){
            System.out.println(listIterator.previous());
        }

        /**
         * Lambda表达式
         * */
        System.out.println("Lambda表达式:");
        list.forEach(s -> System.out.println(s));
    }

Queue

Queue用于模拟队列这类数据结构,队列采用FIFO(先进先出)原则。

Queue定义的方法:

public interface Queue<E> extends Collection<E> {
    //队列末尾添加元素
    boolean add(E e);
    //将指定的元素添加到队列尾部,当使用有容量限制的队列时,此方法通常比 add(Object e)方法更好。
    boolean offer(E e);
    //移除队列头部的元素,但不删除
    E remove();
    //获取队列头部的元素,并删除该元素
    E poll();
    //获取队列头部的元素
    E element();
    //获取队列头部的元素,但是不删除
    E peek();
}

LinkedList实现类

LinkedList类是List接口的实现类,也就是说LinkedList可以根据索引来随机访问放入其中的元素,除此以外LinkedList也可以被当做栈和队列使用。

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<String>();
        //将字符串加入队列末尾
        linkedList.offer("Java");
        //将一个字符串加入栈的顶部
        linkedList.push("Spring");
        //将字符串添加到队列头部(相当于栈的顶部)
        linkedList.offerFirst("HTML");
        //当做list遍历
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.get(i));
        }
        //访问并不删除栈顶的元素
        System.out.println(linkedList.peekFirst());
        //访问并不删除队列的最后一个元素
        System.out.println(linkedList.peekLast());
        //将栈顶的元素弹出栈
        System.out.println(linkedList.pop());
        //输出[Spring, Java]
        System.out.println(linkedList);
        //访问并删除队列中的最后一个元素
        System.out.println(linkedList.pollLast());
        //输出[[Spring]]
        System.out.println(linkedList);
    }
}

上面的代码分别演示了LinkedList作为List,双端队列及栈的使用,所以LinkedList是一个功能非常强大的类,但是需要注意的是,虽然LinkedList功能强大,开发中如果要当做队列使用,就不要调用栈和list相关的方法,这样做会引起不必要的麻烦以及代码难以维护。

本文作者:西瓜
本文链接:https://xiguayaaaaa.github.io/2022/08/02/%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6List%E7%AC%94%E8%AE%B0/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可