Java-集合(2)

List集合

List接口是Collection接口的子接口

  1. List集合类的元素是有序的(添加顺序和取出顺序是一致的)且可重复
  2. List集合类的每个元素都有对应的索引(和数组索引是一样的)

List集合的常用方法

  1. add(int index Object ele):在index索引位置插入ele元素
  2. addAll(int index Collection ele):在index索引位置将ele的所有元素添加进来
  3. get(int index ):获取在index位置的元素
  4. IndexOf(Object obj):返回obj在集合中首次出现的索引
  5. lastIndexOf(Object obj):返回obj在集合中最后一次出现的索引
  6. remove(int index):删除index索引处的元素,且返回这个元素
  7. set(int index Object ele):将index索引处的元素替换为ele
  8. subList(int fromIndex int toIndex):返回从fromIndex处到toIndex前的元素,返回为一个子集合(含头不含尾)
    方法使用演示:
public class test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("王大");
        list.add("王三");
//        1. add(int index Object ele):在index索引位置插入ele元素
        list.add(1,"王二");
        System.out.println(list);
//        2. addAll(int index Collection ele):在index索引位置将ele的所有元素添加进来
        List list2 = new ArrayList();
        list2.add("李大");
        list2.add("王二");
        list.addAll(3,list2);
        System.out.println(list);
//        3. get(int index ):获取在index位置的元素
        System.out.println(list.get(2));
//        4. IndexOf(Object obj):返回obj在集合中首次出现的索引
        System.out.println(list.indexOf("王二"));
//        5. lastIndexOf(Object obj):返回obj在集合中最后一次出现的索引
        System.out.println(list.lastIndexOf("王二"));
//        6. remove(int index):删除index索引处的元素,且返回这个元素
        System.out.println(list.remove(2));
//        7. set(int index Object ele):将index索引处的元素替换为ele
        list.set(0,"牛大");
        System.out.println(list);
//        8. subList(int fromIndex int toIndex):返回从fromIndex处到toIndex前的元素,返回为一个子集合(含头不含尾)
        List list3 = list.subList(1,2);
        System.out.println(list3);
    }
}

输出结果:

[王大, 王二, 王三]
[王大, 王二, 王三, 李大, 王二]
王三
1
4
王三
[牛大, 王二, 李大, 王二]
[王二]

List集合练习

  1. 题目:添加10个以上的元素,在2号位置插入一个元素"好好学习"。
    获取第5个元素,删除第6个元素,修改第7个元素为“天天向上”。最后使用迭代器遍历集合,(使用ArrayList完成)
public class Work1 {
    public static void main(String[] args) {
        List list = new ArrayList();
        for (int i = 0; i < 12; i++) {
            list.add("hello"+i);
        }
        System.out.println(list);
        //2号位置插入一个元素
        list.add(1,"好好学习");
        //获取第5个元素
        System.out.println(list.get(4));
        //删除第6个元素
        list.remove(5);
        System.out.println(list);
        //修改第7个元素为“天天向上”
        list.set(6,"天天向上");
        System.out.println(list);
        //使用迭代器遍历
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println(next);
        }
    }
}
  1. 使用List集合实现类添加三本图书,遍历输出,打印如下效果:
    名称: 价格: 作者:
    名称: 价格: 作者:
    名称: 价格: 作者:
    按照价格排序,从低到高(使用冒泡法)
    要求使用ArrayList,LinkedList 和 Vector三种集合实现
public class Work2 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(new Book("三国演义",20,"罗贯中"));
        list.add(new Book("西游记",25,"吴承恩"));
        list.add(new Book("红楼梦",18,"曹雪芹"));
        list.add(new Book("java入门",30,"高斯"));
        list.add(new Book("java放弃",67,"高斯林"));
        //按照价格排序
        for (int i = 0; i < list.size(); i++) {
            for (int j = 0; j < list.size()-1-i; j++) {
                Book book1 = (Book) list.get(j);
                Book book2 = (Book) list.get(j+1);
                if (book1.price> book2.price){
                    list.set(j,book2);
                    list.set(j+1,book1);
                }
            }
        }
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
class Book{
    String name;
    double price;
    String author;
    public Book(String name, double price, String author) {
        this.name = name;
        this.price = price;
        this.author = author;
    }
    @Override
    public String toString() {
        return "{" +
                "名称= " + name + "\t" +
                " 价格=" + price +"\t"+
                " 作者=" + author  +
                '}';
    }
}

输出结果:
{名称= 红楼梦 价格=18.0 作者=曹雪芹}
{名称= 三国演义 价格=20.0 作者=罗贯中}
{名称= 西游记 价格=25.0 作者=吴承恩}
{名称= java入门 价格=30.0 作者=高斯}
{名称= java放弃 价格=67.0 作者=高斯林}

List集合的三种遍历方式

  1. 迭代器遍历
public class test2 {
   public static void main(String[] args) {
       List list = new ArrayList();
       list.add("天天");
       list.add("向上");
       //迭代器
       Iterator iterator = list.iterator();
       while (iterator.hasNext()) {
           Object next =  iterator.next();
           System.out.println(next);
       }
   }
}
  1. 增强for遍历
       List list = new ArrayList();
       list.add("天天");
       list.add("向上");
       //增强for
       for (Object o:list) {
           System.out.println(o);
       }
  1. 普通for遍历
       List list = new ArrayList();
       list.add("天天");
       list.add("向上");
       //增强for
       for (int i = 0; i < list.size(); i++) {
           System.out.println(list.get(i));
       }

ArrayList集合实现类

ArrayList的注意事项

  1. ArrayList可以加入空元素 null,且可以加入多个
  2. ArrayList是由数组来实现数据存储的
  3. ArrayList基本等同于Vector,ArrayList线程不安全但是执行效率高,在多线程的情况下不建议使用ArrayList

ArrayList扩容机制

  1. ArrayList中维护了一个Object类型的数组elementDate
  2. 当创建ArrayList对象时,如果使用的时无参构造器,则初始elementDate容量为0,第1次添加则扩容elementDate为10,如果后续添加满了,需要再次添加,则会扩容1.5倍
  3. 如果创建ArrayList时,使用的是指定大小的构造器,则初始elementDate为指定大小,如需再次扩容,会扩容1.5倍

ArrayList无参构造创建及扩容底层源码分析

ArrayList list = new ArrayList();
for(int i = 1 ; i<=10;i++){
list.add(i);
}
for(int i = 11 ; i<=15;i++){
list.add(i);
}

可以看到上面的代码,使用ArrayList的无参构造器创建了一个对象list,然后使用for循环依次添加1~10,再次使用for循环添加11 ~15.
下面开始从源码方面分析,到底是怎么扩容的,又是怎么初始化大小的。(下列源码分析,JDK版本不同会导致源码不同,实际需自己debug分析)

  1. 无参构造器创建空数组elementDate
    在这里插入图片描述
    分析:根据前面的学习我们知道ArrayList集合类,是使用数组保存数据的。而elementDate就是这个数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个默认的空数组【0】,所以此时elementDate数组就是【0】。
    2.因为是使用i进行添加,所以首先会进行一个Integer类型的装箱。
    在这里插入图片描述

3.进入add方法,然后调用ensureCapacityInternal(size + 1);方法,确认是否需要扩容:这里的size可以理解为数组当前的长度,在ensureCapacityInternal方法中会判断size + 1减elementDate.length是否大于0,如果大于0就代表当前数组已经满了,不能添加了,就会使用grow扩容。然后再添加,如果小于0就代表还可以添加,就直接添加。
在这里插入图片描述
4.进入ensureCapacityInternal(minCapacity);首先调用calculateCapacity(Object[] elementData, int minCapacity)方法传入elementDate空数组和minCapacity最小默认值 1 .然后使用if判断elementDate是不是等于默认空数组,如果是就返回 Math.max方法传入DEFAULT_CAPACITY默认最小值 10 和minCapacity 1 ,进行比较然后返回最大值,此时会返回DEFAULT_CAPACITY 的值 也就是 calculateCapacity方法会返回 10 ,(如果不是第一次添加就会返回minCapacity 的值)。
在这里插入图片描述

5.ensureCapacityInternal(minCapacity);是将calculateCapacity方法返回的值作为参数传入ensureExplicitCapacity方法,所以接下来进入ensureExplicitCapacity方法:首先modCount++模式计数加1(可以作为添加次数计数),然后使用if判断minCapacity 减elementData.length是不是大于0(10 - 0),如果是大于0就调用 grow方法把minCapacity传进去

在这里插入图片描述
6. grow(真正的扩容方法):将element的长度值给int oldCapacity 实际容量,然后将oldCapacity 实际容量+oldCapacity 实际容量>>1(位运算,除以2)也就是将oldCapacity 实际容量的1.5倍给 newCapacity 新容量。但是呢 因为是第一次,此时的oldCapacity 实际容量是0 ,0+0/2 还是等于0 .所以newCapacity 新容量也是0 ,因此下面还使用了一个if判断来防止第一次扩容失败,它使用if判断 如果newCapacity 新容量(0)减minCapacity (此时是10 )小于0的话,就表示 是第一次扩容,就将minCapacity的值给newCapacity 新容量,所以此时newCapacity 新容量的值被改成了10 ,接着又进行一个if判断,看看newCapacity 新容量是不是比最大的值还大,(这个先不分析)。最后使用Array.copy方法传入elementDate数组,和newCapacity 新容量。将原数组的数据复制且扩容大小为newCapacity 新容量的值,至此第一次数组扩容完成,elementDate数组从0变成了10

在这里插入图片描述
7.grow执行完毕返回ensureExplicitCapacity方法
在这里插入图片描述

  1. ensureExplicitCapacity方法执行完毕返回ensureCapacityInternal

在这里插入图片描述

9.ensureCapacityInternal方法执行完毕返回add方法,执行elementData[size++] = e;这里因为初始size是0且是后++,所以是在 elementData的0号下标添加传入的元素。然后size++,这样下次再进入添加的位置就会往后移动一位,不会覆盖。

add执行流程图

在这里插入图片描述
重点方法的作用:
calculateCapacity(Object[] elementData, int minCapacity):确定是否是空数组,如果是空数组就返回10 如果不是空数组就返回elementDate的长度+1

ensureExplicitCapacity(int minCapacity):根据calculateCapacity方法返回的值减去当前elementDate的长度,看够不够,如果够就直接返回add进行添加,如果不够就进入grow进行扩容,然后再返回add进行添加

grow(int minCapacity):对elementDate进行1.5倍的扩容(使用Arrays.copyOf),同时根据传入的minCapacity判断是否是第一次的空数组

ArrayList有参构造创建及扩容底层源码分析

 ArrayList list = new ArrayList(8);
        for(int i = 1 ; i<=10;i++){
            list.add(i);
        }
        for(int i = 11 ; i<=15;i++){
            list.add(i);
        }
  1. 进入有参构造器,将传入的值给initialCapactity初始容量,如果初始容量大于0就直接把elementDate数组变初始指定的大小
    如果初始容量 = 0 就还是给默认空数组后面再添加就跟无参构造创建添加一样 如果小于0就抛出异常
    在这里插入图片描述
  2. 接着还是对i 进行装箱,之后添加add方法,传入size+1(0+1),进入ensureCapacityInternal方法
    在这里插入图片描述
  3. 进入ensureCapacityInternal方法后还是首先调用ensureExplicitCapacity方法传入elementDate数组和size+1(0+1)
    在这里插入图片描述
  4. 进入ensureExplicitCapacity跟无参创建add一样:首先修改次数+1,然后用size+1(0+1)减去当前数组的容量看够不够用,如果够就直接返回到add进行添加即可
    如果不够就进行扩容,因为这里是第一次添加而需要的空间是0+1 且当前数组的长度是8,所以肯定是够的,下面直接快进到第9次添加继续debug
    在这里插入图片描述
    5.当第9次add后,进入ensureExplicitCapacity方法,此时需要的空间是9 而elementDate的长度是9 .9-9小于0,所以会进入grow进行扩容
    在这里插入图片描述
    6.进入grow,minCapacity需要容量是9 然后把elementDate的长度给新容量,接着将新容量*1.5倍,此时新容量是12
    然后if判断 新容量12 - 需要的容量9 大于0 所以不用把需要的容量给新容量。接着直接进行下面的 Arrays.copyOf()传入原数组且扩容
    在这里插入图片描述
    执行流程图:
    在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值