List体系

        查阅API,看List的介绍。有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与 set 不同,该列表通常允许重复的元素。
        看完API,我们总结一下:
        List接口:

        它是一个元素存取一致的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
        它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
        集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

        那么下面我们重点来学习List接口两个重点儿子 ArrayList和LinkedList类。

1.ArrayList

         先来看一下ArrayList中特有方法。

ArrayList<E>中的方法(这里的E可以当作为Object类来理解,我这里举例使用String类型):
        public boolean add(E e):添加元素到列表末尾
        public void add(int index, E element):将指定元素添加到指定索引位置,该位置原有的元素及之后元素会后移
        public E get(int index):将指定索引位置的元素取出
        public E set(int index, E element):将指定位置的元素设置成新的值,并且返回该索引原有的值
        public E remove(int index):将指定索引位置的元素删除,并且返回要删除的元素

public class ArrayListDemo01 {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<>();
        al.add("张三");
        al.add("李四");
        //1.将指定元素添加到指定索引位置
        al.add(1,"王五");
        System.out.println(al);
        //2.将指定索引位置的元素取出
        System.out.println(al.get(2));
        //3.将指定位置的元素设置成新的值,并且返回该索引原有的值
        System.out.println(al.set(1,"赵六"));
        //4.将指定索引位置的元素删除,并且返回要删除的元素
        al.remove(0);
        System.out.println("经过上述一系列操作后集合中元素:"+al);
    }
}

1.1  ArrayList原理

ArrayList底层原理
        1.ArrayList从名字上推断(Array)底层使用的是数组结构
        2.new ArrayList()的时候,底层会创建一个空数组,即Object[] elementData =new Object[0];
        3.当首次调用add方法的时候,底层会开辟一个默认容量为10的Object数组
此时,Object[] elementData = new Object[10],用来存储第一个元素。

        对ArrayList<String> al = new ArrayList<>();进行Debug强制进入,如下图:

        而不难发现,这里的elementDate是一个Object类型的数组,且初始化为{ }。该数组的默认容量为10,即:

        再增加元素之后,直接打印集合会调用重写后的toString方法,如下图,可以看到,创建了一个迭代器,如果! it.hasNext()为true,即没有下一个元素,直接打印[ ]。否则往下执行,会创建一个StringBuilder容器,先把“【”加上,再遍历判断当前元素e是集合本身还是下一个普通元素,如果是普通元素就直接调用append追加再后面,最后跟上“】”,完成元素的添加。

        ArrayList的扩容原理类似于StringBuilder,这里不再展示具体操作,可以去参考StringBuilder的扩容解释,下面用图来理解ArrayList如何扩容:

1.2  ArrayList特点

        1.集合中元素有索引,有顺序,可以存储重复元素

        2.ArrayList集合增删慢,查询快  
  a.查询快:因为ArrayList底层是一个数组,而数组有索引,我们根据索引可以直接取出元素
          集合索引      0           1           2        3     4  ...   9
     ArrayList元素: "abc"    "def"     "ghk"   null  null ... null
     获取"ghk":根据索引直接可以取出元素 get(2)

   b.增删慢:如果我们要增删一个元素,会导致这个元素后面的所有元素往前或者往后挪动,可能会移动大量元素,导致ArrayList增删效率低
          集合索引                             0          1          2         3     4  ...  9
     ArrayList元素:                       "abc"   "def"   "ghk"  null null .... null
     添加一个"ml"到0索引位置      "ml"    "abc"   "def"  "ghk" null ... null
     当我们添加一个元素的时候,该元素之后的元素都要依次往后移动一个位置

          集合索引            0            1        2           3     4  ...  9
    ArrayList元素:         "abc"   "def"   "ghk"      null null .... null
    删除"abc"               "def"  "ghk"    null          null null .... null
            当我们删除一个元素的时候,该元素之后的元素都要依次往前移动一个位置

也可以看图片理解:

 1.3  ArrayList的三种遍历方式

        对于ArrayList集合的遍历,有常用的三种方法。分别是:迭代器遍历、(特有遍历方式)索引遍历、增强for遍历。
        首先看第一种,迭代器遍历:

public class ArrayListDemo03 {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<>();
        al.add("abc");
        al.add("def");
        al.add("qwe");
        Iterator<String> iterator = al.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

        查看API发现ArrayList中有迭代器方法,那就可以使用迭代器来遍历集合中的元素,使用集合对象al来创建迭代器,再使用迭代器的hasNext( )判断是否有下一个元素,使用next()打印下一个元素,针对于迭代器的这两个方法:

        其实刚开始的时候hasNext()的指针指向的是第一个元素上方,此时判断存在下一个元素的时候,指针移动指向第一个元素。如图:

        当hashNext( )指向最后一个元素的时候,再进行判断就会返回false值,此时循环就停止了。也就循环遍历结束。

        最后运行结果图:

        再看第二种方式:索引遍历,也是ArrayList特有的遍历(其他集合没有索引)。

public class ArrayListDemo03 {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<>();
        al.add("abc");
        al.add("def");
        al.add("jdk");
        for (int i = 0; i < al.size(); i++) {
            System.out.println(al.get(i));
        }
    }
}

        这里的size方法和get方法上面提到过,size获取集合中有效元素数,get方法可以传入索引来返回指定的元素。

运行结果:

        最后看第三种:增强for遍历

public class ArrayListDemo03 {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<>();
        al.add("张三");
        al.add("李四");
        al.add("王五");
        for (String element:al){
            System.out.println(element);
        }
    }
}

        增强for语法:for(所遍历的数据类型  变量名:所遍历集合),所以直接输出结果为:

        其实对于ArrayList存储自定义引用类型,存储的仍是地址值,而并非对象本身。(这里省略了部分代码)直接可以看下图理解:
 

2.LinkedList

2.1  LinkedList原理

        LinkedList是基于一种双向链表的实现。LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法,所有操作的执行都与双向链表的预期相同。可以看下图来理解LinkedList的实现原理。

再来说说LinkedList<E>中特有的方法:
        public void addFirst(E e):向集合的开头位置添加元素
        public void addLast(E e):向集合的末尾添加元素
        public E getFirst():获取集合头部的元素
        public E getLast():获取集合尾部的元素
        public E get(int index):取出指定位置的元素
        public E removeFirst():移除头部元素,并且返回当前被删除的元素
        public E removeLast():移除尾部的元素,并且返回当前被删除的元素

public class LinkedListDemo01 {
    public static void main(String[] args) {
        LinkedList<String> ll = new LinkedList<>();
        ll.add("abc");
        ll.add("def");
        //1.每一次都添加到最前面
        ll.addFirst("ghk");
        System.out.println(ll);//[ghk, def, abc]
        //2.向集合的末尾添加元素
        ll.addLast("poo");
        System.out.println(ll);//[ghk, abc, def, poo]
        //3.获取集合头部的元素
        System.out.println(ll.getFirst());//ghk
        System.out.println(ll);//[ghk, abc, def, poo]
        //4.获取集合尾部的元素
        System.out.println(ll.getLast());//poo
        System.out.println(ll);//[ghk, abc, def, poo]
        //5.取出指定位置索引元素
        System.out.println(ll.get(1));//abc
        System.out.println(ll);//[ghk, abc, def, poo]
        //6.移除头部元素,并且返回当前被删除的元素
        System.out.println(ll.removeFirst());//ghk
        System.out.println(ll);//[abc, def, poo]
        //7.移除尾部的元素,并且返回当前被删除的元素
        System.out.println(ll.removeLast());//poo
        System.out.println(ll);//[abc, def]
    }
}

    

2.2  LinkedList特点

        这里主要分析LinkedList基于双向链表的方式实现元素的增加删除查询。

       LinkedList增加或删除元素,效率相对于ArrayList较高, ArrayList每次添加 或删除 动作,可能需要移动大量的元素而LinkedList只需要改变元素记录的地址值就可以了
         尽管双向链表查询既能从头开始找,也能从尾开始找,但是LinkedList 查询效率相对于ArrayList 较慢,因为ArrayList可以根据索引直接取出元素,而LinkedList需要 顺链 或 逆链 杳找,

        具体看图理解:

3.集合的另一种初始化方式

        对于集合的初始化方式,我们一般写 ArrayList<String> al = new ArrayList<>();后再使用add方法增添元素,al.add("abc");,而另一种初始化方式我们也应能看懂和使用,如下:

public class ArrayListDemo02 {
    public static void main(String[] args) {
        //1.一般初始化方式
        ArrayList<String> al = new ArrayList<>();
        al.add("abc");
        al.add("def");
        System.out.println(al);
        
        //2.双大括号形式初始化集合
        ArrayList<String> al02 = new ArrayList<>(){
            {
                add("abc");
                add("def");
            }
            //相当于打印对象,但是最终还是会调用toString方法
        };
        System.out.println(al02);
        System.out.println(al02.getClass());
    }
}

        运行结果:

        代码中的1.中的初始化方式就是我们常用的,2.中的大括号形式就是另一种,对于理解,我们可以把大括号形式的初始化看成是:

class ArrayListDemo02$1 extends ArrayList<String>{
    {
        //构造代码块
        add("abc");
        add("def");
    }
    //继承了ArrayList的toString方法
}
ArrayList<String> al = new ArrayListDemo02$1(); 

        这种初始化方式相当于底层创建了一个名为 ArrayListDemo02$1 继承了Arraylist,然后通过父类ArrayList实现子类   ArrayListDemo02$1的对象al,我们可以通过getClass( )方法来验证一下:

        打印输出的class arraylist01.ArrayListDemo02$1也验证了我们的说法,对象al02引用指向对象使用的类是ArrayListDemo02$1,而不是直接输出的ArrayList,于我们前面所学到的匿名内部类的思想一样。针对于运行的原理,其实当我们new对象的时候,会先走构造代码块,构造代码块中有添加元素的方法,相当于把元素添加到集合中。

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值