【学习笔记】集合(二)

集合(二)

 

List 集合

  • List 是 Collection 的子接口

  • 特点:有序、有下标、元素可以重复

  • 它除了Collection 中的方法外,还有一些自己特有的方法

    • void add(int index,Object o) //在index位置插入对象o

    • boolean addAll(int index,Collection c) //将一个集合中的元素添加到此集合的index位置

    • Object get(int index) //返回集合中指定位置的元素

    • List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素

    • listIterator() //方法更多的迭代器

    • listIterator(int index) //从集合的指定位置开始遍历

    • boolean remove(int index) //移除指定位置的元素

    • boolean remove(Object o) //从此列表移除第一次出现的指定元素

     

添加元素,把元素添加到指定位置,以及删除某个位置的元素

package com.collection.listDemo;
​
import java.util.ArrayList;
import java.util.List;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        List list = new ArrayList();
        //添加元素,把元素添加到指定位置
        list.add("iphone");
        list.add("小米");
        list.add(0,"华为");
        System.out.println(list);
        list.remove(1);
        System.out.println(list);
    }
}
​

image-20220723161351906

遍历:由于集合有下标,所以可以使用for循环来遍历集合,也可以使用 listIterator 来遍历集合

  • listIterator 中特有的方法

    • add(E e) //插入元素

    • hasPrevious() //如果以逆向遍历集合,迭代器中有多个元素,就返回true

    • nextIndex() //返回对应元素的下标

    • previous() //返回列表中前一个元素

    • previousIndex() //逆向遍历时,返回对应元素下标

    • set(E e) //用指定元素替换所遍历的元素

package com.collection.listDemo;
​
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        List list = new ArrayList();
        //添加元素,把元素添加到指定位置
        list.add("iphone");
        list.add("小米");
        list.add(0,"华为");
        System.out.println(list);
        //使用listIterator 遍历集合
        System.out.println("正向");
        ListIterator it = list.listIterator();
        while (it.hasNext()){
            System.out.println(it.nextIndex()+":"+it.next());
        }
        System.out.println("逆向");
        while (it.hasPrevious()){
            System.out.println(it.previousIndex()+":"+ it.previous());
        }
    }
}

image-20220723164915870

 

注意:在使用逆序遍历之前,必须先正序遍历,否则会没有内容

原因是,在listIerator初始时,有一个指针默认是在集合的第一位,这时候如果再去逆向遍历,指针就会向前走,就不会去遍历数组

 

判断:和Collection 一样 有 contain() 和 isEmpty() 两个方法,使用方法也相同

 

获取位置:在list 中独有的方法 indexOf() 它可以获取元素在集合中的位置

package com.collection.listDemo;
​
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        List list = new ArrayList();
        //添加元素,把元素添加到指定位置
        list.add("iphone");
        list.add("小米");
        list.add(0,"华为");
        System.out.println(list);
        //获取位置
        System.out.println("华为在集合中的位置:"+list.indexOf("华为"));
    }
}

image-20220723170440802

 

 

list 在保存 数字数据时:

  • 保存数字数据时,会有自动装箱的操作,因为集合中只能保存引用数据类型,保存不了基本数据类型

  • 在进行删除数字数据时,如果你直接往remove方法中传入这个数字时,如果这个数字超过了集合的下标时,就会报下标越界异常,就算没有超出集合的下标,那么删除的哪个数据,也有可能不是你想要删除的数据,而是数字对应的下标的数据。

    • 这个问题我们有两种方法解决

      • 强制类型转换成Object

      • new Integer(数字)

package com.collection.listDemo;
​
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        List list = new ArrayList();
        //添加数字元素
        list.add(10);
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        System.out.println("集合中元素的个数:"+list.size());
        System.out.println(list);
        list.remove((Object) 20);
        System.out.println("集合中元素的个数:"+list.size());
        System.out.println(list);
        list.remove(new Integer(30));
        System.out.println("集合中元素的个数:"+list.size());
        System.out.println(list);
    }
}

image-20220723172610400

 

补充方法:subList(int fromIndex,int toIndex) 返回子集合,下标从fromIndex到toIndex 含头不含尾

package com.collection.listDemo;
​
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        List list = new ArrayList();
        //添加数字元素
        list.add(10);
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        System.out.println("集合中元素的个数:"+list.size());
        System.out.println(list);
        List list1 = list.subList(2,4);
        System.out.println(list1);
    }
}

image-20220723173211173

 

List 实现类

 

  • ArrayList:

    • 数组结构实现,查询快、增删慢

    • JDK1.2版本加入,运行效率快、线程不安全

  • Vector:

    • 数组结构实现,查询快、增删慢

    • JDK1.0版本加入,运行效率慢、线程安全

  • LinkedList:

    • 链表结构实现,增删快,查询慢

 

 

ArrayList 方法

  • ArrayList的增加、删除、迭代、判断、查找与List相同

我们下面看一下在remove 和 contains 里面直接new 一个对象

package com.collection.listDemo.arrayList;
​
import com.collection.collectionDemo.Student;
​
import java.util.ArrayList;
​
public class Demo01 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        Student s1 = new Student("张三",22);
        Student s2 = new Student("李四",23);
        Student s3 = new Student("王五",20);
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
​
        arrayList.remove(new Student("张三",22));
        System.out.println("元素的个数:" + arrayList.size());
        System.out.println(arrayList);
    }
}

image-20220723183132862

 

我们发现,新new的对象没有被删除,尽管它的属性和s1相同,我们如果想要将属性重复的对象在集合中删除,就需要重写equals方法,去判断两个对象的属性是否相等。

image-20220723183339847

 

在执行上面那段代码,就会发现属性相同的对象被删除了

image-20220723183433149

contain() 方法与之相同

 

ArrayList 源码分析

 

  • 常量:

    • DEFAULT_CAPACITY = 10; //默认容量 为10

    • elementData 存放元素的数组

    • size 实际的元素个数,一开始一定小于默认容量

    • private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    • private static final Object[] EMPTY_ELEMENTDATA = {};

  • 构造方法(无参):

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

先把空的数组赋值给存放数据的数组 elementData

所以没有向集合中添加任何元素时,数组容量为0

 

  • add() 方法 //添加元素

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

这时候size是0,我们传入的参数是1

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

这个方法先调用calculateCapacity,传入的参数是,存放元素的数组elementData(此时这个数组为空)、add方法传入的参数 1

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

这个方法先判断elementData是否与DEFAULTCAPACITY_EMPTY_ELEMENTDATA 相等,(第一次调用肯定相等,因为在构造方法中就把DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给了elementData),如果相等,就返回默认容量和传入参数中大的那一个,(第一次调用,默认容量为10,是大的那个数),如果不相等就返回传入参数

上面的方法ensureCapacityInternal 接收到值之后,调用ensureExplicitCapacity

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
​
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

如果传入的参数大于elementData的长度就调用grow方法

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    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);
}

第一次调用:oldCapacity = 0,newCapacity = 0,minCapacity = 10

newCapacity - minCapacity 肯定小于0,所以 newCapacity 被赋值为minCapacity =10

但是newCapacity - MAX_ARRAY_SIZE(int 类型能表示的最大值) 肯定小于0

然后给把newCapacity 的长度复制给 elementData 数组

所以第一次向集合中插入数据时,数组的长度为10,size = 1

 

然后我们看第二次插入数据时:

  • add 方法中 size = 1;然后传入的参数为 2

  • calculateCapacity 方法中 elementData 不等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以返回的是 minCapacity 也就是 2

  • ensureExplicitCapacity 方法中,minCapacity - elementData.length = -8,不大于0,所以不用调用grow()

 

当数组存满时,也就是达到了默认容量的时候,就需要扩容了

  • add 方法中 size = 10;然后传入的参数为 11

  • calculateCapacity 方法中 elementData 不等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以返回的是 minCapacity 也就是 11

  • ensureExplicitCapacity 方法中,minCapacity - elementData.length = 11-10,大于零调用grow

  • grow 方法中,oldCapacity = 10,newCapacity = 15,直接把newCapacity = 15 复制给elementData,elementData的长度也就变成了15

 

Vector 方法

vector 中的方法与Arraylist 中的方法基本相同,不同的是在遍历集合时,vector可以使用枚举器来遍历

package com.collection.listDemo.vector;
​
import java.util.Enumeration;
import java.util.Vector;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        Vector v = new Vector();
        //添加元素
        v.add("张三");
        v.add("李四");
        v.add("王五");
        v.add("赵六");
        //遍历集合
        Enumeration en = v.elements();
        while (en.hasMoreElements()){
            String str = (String) en.nextElement();
            System.out.println(str);
        }
    }
}

image-20220724090304997

 

  • 其他方法:

    • vector.firstElement //获取第一个元素

    • vector.lastElement //获取最后一个元素

    • vector.elementAt //获取某个位置的元素

package com.collection.listDemo.vector;
​
import java.util.Enumeration;
import java.util.Vector;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        Vector v = new Vector();
        //添加元素
        v.add("张三");
        v.add("李四");
        v.add("王五");
        v.add("赵六");
        //遍历集合
​
        System.out.println(v.firstElement());
        System.out.println(v.lastElement());
        System.out.println(v.elementAt(2));
​
    }
}

image-20220724091058851

 

 

LinkedList 方法

  • 创建集合,添加、删除、遍历元素,判断,获取

package com.collection.listDemo.linkList;
​
import com.collection.collectionDemo.Student;
​
import java.util.*;
​
public class Demo01 {
    public static void main(String[] args) {
        //创建集合
        LinkedList link = new LinkedList();
        //创建学生对象
        Student s1 = new Student("张三",15);
        Student s2 = new Student("李四",18);
        Student s3 = new Student("王五",12);
        //把学生对象添加到集合中
        link.add(s1);
        link.add(s2);
        link.add(s3);
        System.out.println(link);
        //删除元素
//        link.remove(s1);
//        link.remove(1);
//        link.remove(new Student("李四",18));
//        System.out.println(link);
//        link.clear();
        //遍历集合
        //1.for循环
        System.out.println("-------for循环---------");
        for (int i = 0; i < link.size(); i++) {
            System.out.println(link.get(i));
        }
        //2.forEach循环
        System.out.println("-------forEach循环---------");
        for (Object o:
             link) {
            System.out.println(o);
        }
        //3.Iterator迭代器
        System.out.println("-------Iterator迭代器---------");
        Iterator it = link.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
       //4.listIterator迭代器
        System.out.println("-------listIterator迭代器---------");
        ListIterator lit = link.listIterator();
        System.out.println("正向");
        while (lit.hasNext()){
            System.out.println(lit.nextIndex()+":"+lit.next());
        }
        System.out.println("逆向");
        while(lit.hasPrevious()){
            System.out.println(lit.previousIndex()+":"+lit.previous());
        }
        //判断
        System.out.println("是否含有对象s2:"+link.contains(s2));
        System.out.println("集合是否为空:"+link.isEmpty());
        //获取
        System.out.println("s1在集合中的位置:"+link.indexOf(s1));
    }
}

image-20220724095951729

 

LinkList 源码

  • int size = 0; //集合的大小

  • Node first //链表的头节点

  • Node last //链表的尾节点

  • Node 类

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;
    }
}

item: 是当前的元素

next :下一个节点

prev: 前一个结点

  • add() 方法

public boolean add(E e) {
    linkLast(e);
    return true;
}

add方法 调用 linkLast 方法 并且把要添加的元素传进去

 

void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

第一次调用:

  • 把last 赋值给 l ,这时 l 为null,然后创建一个节点,这个节点的next 和 prev 都为 null,因为只有它一个节点。

  • 然后把newNode 赋值给 last ,也就是last指向新创建的节点newNode

  • 判断 l 是否为空,为空的话,就把first 也指向 新节点 newNode

第二次调用:

  • 把last 赋值给 l ,这个时候 l 不为空,l 是我们第一次创建的节点,创建第二个节点,第二个节点的next 为空,但prev 为 l,也就是第一个节点

  • 然后把第二个节点赋值给last,也就是last指向第二个节点

  • 判断 l 是否为空,这时候 l 不为空,l 为第一个节点,所以我们让第一个节点的next 指向第二个节点

 

删除元素时,就是改变节点之间的指向,没有位置的移动,所以比较快

 

ArrayList 和 LinkList 的区别

image-20220724103613743

 

  • ArrayList:必须开辟连续空间,查询快,增删慢

  • LinkedList:无需开辟连续空间,查询慢,增删快

posted @ 2022-07-24 10:39  GrowthRoad  阅读(37)  评论(0编辑  收藏  举报