ArrayList LinkedList Vector的知识点

List

概述

  1. List集合存储元素的特点:有序可重复

    有序:List集合中的元素有下标。从零开始,以1递增。

    可重复:存储一个1,还可以再存储

  2. List继承了Collection,也就会有自己自带的方法

    List中特有的常用的方法:

    1. void add(int index, Object element) 往列表指定位置添加元素

      使用不多,对ArraysList来说效率太低(涉及到元素的移动)

      //创建list类型的集合
              List myList = new ArrayList();
      
              //添加元素
              myList.add("A");//默认都是往集合末尾添加元素
              myList.add("B");
              myList.add("C");
              myList.add("D");
      
              //在列表指定位置添加元素
              myList.add(1,"Queen");
      
              //迭代
              Iterator it = myList.iterator();
              while (it.hasNext()){
                  System.out.println(it.next());
              }
      
    2. Object get(int index) 根据下标获取元素

      因为有下标,所以List有其独有的遍历方式 Set没有

       for (int i = 0; i < myList.size(); i++) {
                  System.out.println(myList.get(i));
              }
              Object o = myList.get(1);
              System.out.println(o);
      
    3. int indexOf(Object o) 获取指定对象第一次出现处的索引

    4. int lastIndexOf(Object o)获取指定对象最后一次出现出的索引

    5. Object remove(int index)根据下标删除元素

    6. Object set(int index, Object element) 根据下标修改指定位置的元素值

增删改查(english)

增:add,save new

删:delete、drop、remove

改:update、set、modify

查:find、get、query、select

ArrayList

  1. 默认初始化容量为10 (底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量变成10)

  2. ArrayList数组底层是Object类型的数组Object[]

  3. 构造方法

    ​ new ArrayList() 或者 new ArrayList(20)

    //默认初始化容量为10
            List list1 = new ArrayList();
            //集合的size()方法是获取当前集合中的元素的个数,不是获取集合的容量。
            System.out.println(list1.size());//0
    
            //指定初始化容量为20
            List list2 = new ArrayList(20);
            //集合的size()方法是获取当前集合中的元素的个数,不是获取集合的容量。
            System.out.println(list2.size());//0
    
  4. 扩容

    当ArrayList容量满了以后,会在原容量的基础上扩容1/2 原容量的1.5倍 (初始化容量为10 扩容后为15) 如果扩容的太小 会把容量变为minCapacity,如果扩容的太大了,会把MAX_ARRAY_SIZE=Integer.MAX_SIZE - 8;作为新的容量

    ​ 优化:尽可能少的扩容。因为数组的扩容效率比较低,建议在使用ArrayList集合的时候预估元素的个数,给定一个初始化容量(每次扩容都会创建一个大容量的ArrayList,然后把所有的元素复制到新的ArrayList)

  5. 优点:检索效率比较高(每个元素占用空间大小相同,内存地址是连续的,知道首元素内存地址,知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高)

  6. 缺点:随机增删效率比较低,线程不安全,数组无法存储大数据量(很难找到一块非常巨大的连续的内存空间)

  7. 注意:在数组末尾添加元素效率比较高

  8. 面试题:这么多的集合中,你用那个集合比较多?

    ​ ArrayList,因为在实际的使用过程中,用户用来查询的操作会比较多 增删会比较少,大多数场景下会往数组末尾添加元素,效率不会受到影响,如果需要很多的增删,可以使用LinkedList,需要线程安全,那就使用Vector

构造方法
//创建一个初始化容量为10的集合
        List list1 = new ArrayList();

        //创建一个初始化容量为100的集合
        List list2 = new ArrayList(100);

        //创建一个HashSet
        Collection c = new HashSet();

        //往HashSet中添加元素
        c.add(100);
        c.add(300);
        c.add(989);
        c.add(150);

        //通过这个构造方法就可以将HashSet集合转换成List集合。
        List list3 = new ArrayList(c);

        for (int i = 0; i < list3.size(); i++) {
            System.out.println(list3.get(i));
        }

LinkedList

单向链表结构

链表优点:随机增删元素效率较高。(因为增删元素不涉及到大量元素位移)

链表缺点:查询效率较低,每一次查找某个元素的时候都需要从头节点开始往下遍历,不能通过数学表达式的计算来找到被查找元素的内存地址

/*
单链表中的节点。
节点是单向链表中的中基本的单元
每一个节点Node都有两个属性:
一个存储元素,一个存储下一个节点的内存地址
 */
public class Node {
    //存储的数据
    Object element;
    //下一个节点的内存地址
    Node next;

    public Node() {
    }

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

}
public class Link {
    //创建头节点
    Node header = new Node(null,null);

    Node linked = header;

    int size = 0;


    //增加元素的方法
    public void add(Object obj){
        Node node = new Node(obj, null);
        if(header.next == null){
            header.next = node;

        }else {
            linked.next = node;
        }
        linked = node;
        size++;
    }

    //删除元素的方法
    public void remove(Object obj){
        if (size == 0){
            System.out.println("没有元素");
            return;
        }

        Node flag = address(obj);
        if (flag == null){
            System.out.println("没有这个元素");
            return;
        }
        while (flag != null){
            if(flag.next.next == null){
                flag.next = null;
                size--;
            }else {
                flag.next = flag.next.next;
                size--;
            }
            flag = address(obj);
        }

    }

    //修改元素的方法
    public void set(Object obj1,Object obj2){
        if (size == 0){
            System.out.println("没有元素");
            return;
        }
        Node flag = address(obj1);
        if (flag == null){
            System.out.println("没有这个元素");
            return;
        }
        while (flag != null){
            flag.next.element = obj2;
            flag = address(obj1);
        }

        System.out.println("修改成功");
    }

    //查询元素的方法
    public void select(Object obj){
        if (size == 0){
            System.out.println("没有元素");
            return;
        }
        Node flag = header.next;
        if(header.next.element == obj){
            System.out.println(1);
        }else {
            flag = header.next.next;
            int i = 2;
           while (true){

               if(flag.element != obj && flag.next != null){
                   flag = flag.next;
                   i++;
               }else if (flag.element == obj){
                   System.out.println(i);
                   break;
               }else {
                   System.out.println("没有这个元素");
                   break;
               }
           }
        }
    }
//寻找元素地址的方法,返回的是找到元素的前一个node的地址
    public Node address(Object obj){
        Node flag = header;
        if (flag.next == null){
            return null;
        }
            while (true){
                if(flag.next.element != obj && flag.next.next != null){
                    flag = flag.next;
                }else if (flag.next.element == obj){
                    return flag;
                }else {
                    return null;
                }
            }


    }
//打印链表的方法
    public void out() {
        if (size == 0){
            System.out.println("没有元素");
            return;
        }
        Node flag = header.next;
        System.out.print("[");
            System.out.print(" " + flag.element);
            while (flag.next != null ){
                System.out.print(",  " + flag.next.element);
                flag = flag.next;
        }
        System.out.print(" ]");

    }
}
//测试类
public class Test {
    public static void main(String[] args) {
        Link link = new Link();
        loop:
        while(true){
            System.out.println("请选择功能: \n1: 添加元素\n2: 删除元素\n3: 修改元素\n4: " +
                    "查询元素\n5: 输出链表\n0: 退出系统");
            Scanner sc = new Scanner(System.in);
            int a = sc.nextInt();
            Scanner sc2 = new Scanner(System.in);
            switch (a){
                case 1:
                    System.out.println("请输入要添加元素的值:");

                    int x = sc2.nextInt();
                    link.add(x);
                    break;
                case 2:
                    System.out.println("请输入要删除元素的值:");
                    int z = sc2.nextInt();
                    link.remove(z);
                    break ;
                case 3:
                    System.out.println("请输入要修改的元素");
                    int v1 = sc2.nextInt();
                    System.out.println("将这个元素修改为:");
                    int v2 = sc2.nextInt();
                    link.set(v1,v2);
                    break ;
                case 4:
                    System.out.println("请输入要查找元素的值:");

                    int y = sc2.nextInt();
                    link.select(y);
                    break;
                case 5:
                    link.out();
                    break ;
                case 0:break loop;

            }
        }
    }
}

ArrayList:把检索发挥到极致(末尾添加元素的效率还是很高的)

LinkedList:把随机增删发挥到极致。

实际使用过程中,加元素都是往末尾添加,所以ArrayList用的比LinkedList多

源码分析

内存图

内存图

 //头插法
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++;
    }
 //尾插法
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++;
    }

无初始化容量

总结
  1. LinkedList集合是双向链表
  2. 对于链表数据接口来说,随机增删效率较高,检索效率较低
  3. 链表中的元素在空间存储中,内存地址不连续

Vector

  1. 底层是数组

  2. 初始化容量为10

  3. 每次扩容为原来容量的2倍 (ArrayList为1.5倍)

  4. Vector中所有的方法都带synchronized关键字,是线程安全的。效率较低使用较少

  5. 将一个线程不安全的ArrayList集合转换成线程安全的

    使用集合工具类

    java.util.Collection 集合接口

    java.util.Collections 集合工具类

    //创建一个ArrayList
            List myList = new ArrayList();
            
            //编程线程安全的
            List list = Collections.synchronizedCollection(myList);
            
            //myList就变成了线程安全的了
            mylist.add(11);
            mylist.add(12);   
            mylist.add(288);
    

面向接口化编程

不管是LinkedList还是ArrayList,写代码并不需要关心是哪种集合

面向接口编程,调用的方法都是接口中的方法

		//List list = new ArrayList();//这样写表示代码底层你使用了数组
        List list = new LinkedList();//这样写表示代码底层你使用了双向链表

		//以下这些方法面向的都是接口编程
        list.add("123");
        list.add("456");
        list.add("789");

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值