菜鸟学JAVA之——集合框架(一)(List接口的实现类ArrayList)

集合框架

接下来将要学习的内容为:

	1.Collection接口(线性存储)(容器的最高父接口)

​		a)List接口(有序(指有下标/索引)、数据可重复)

​		b)Set接口(无序,数据不可重复)
	
​		c)queue队列

	2.Map接口(键(key)-值(value)对存储)(映射关系)

首先了解一个设计模式:适配者模式(通俗的理解是功能基本不变,只是改了方法名)(最主要的功能就是将一个接口转换为另一个接口)

List接口的实现类:

  1. ArrayList(底层为数组结构)

    在数组中赋值方式为a[0] = 1; ArrayList底层还是数组,数组赋值是通过脚标赋值的,我现在还要用他赋值的功能,但是对外提供接口变了,赋值变为了add();

    ​ 优点:查、改效率高

    2.LinkedList(底层为双向链表)

    ​ 优点:增、删效率高

    ​3.Vector(和ArraysList基本上一样(数组结构),只不过他是线程安全的,访问效率低)

自己实现容器就需要实现Collection接口,并且实现所有需要实现的方法。Collection没有get()方法,所以不能定位。它也没有set()方法。但List里面有这两个方法

ArrayList

ArrayList 无参的构造方法,创建了一个长度为0的数组

ArrayList list = new ArrayList();//默认容量为0

也有有参的构造方法,指定了初始大小

本质上就是创建了一个参数大小的数组

ArraysList list = new ArrayList(10);//容量设置为10
//还可以传一个集合,将传进的集合中的元素copy给新集合

来看看源码

public ArrayList(int initialCapacity) {  //创建时可传参
        if (initialCapacity > 0) {  //如果传的参数容量大于0
            this.elementData = new Object[initialCapacity];  //创建一个大小为initialCapacity的数组
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA; //如果传入的参数为0,那就是默认的那个空数组
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+  //如果参数小于0则抛出一个异常
                                               initialCapacity);
        }
    }

1.添加元素(add())

添加过程:

  1. 先将底层数组扩容
  2. 将元素赋值到数组对应位置
  3. 返回true
list.add(10);//这里的这个10是基础数据类型的对象类型:Integer类型

list.add("abc")//添加元素的位置跟add的次序有关,第一个add在数组的第一个位置

源码

 public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e; //核心代码:把这个元素赋给了数组相应位置。
        return true; //添加成功,返回一个true。如果添加不成功在上一行代码中就会报错,不会执行到这一行
    }
//这就是适配者模式,把给数组赋值的功能的名称转为了add,外面添加元素调用add方法就行了,功能没有变

add还可以给指定位置添加元素

//为指定位置添加元素   第一个参数:指定位置,第二个参数:元素值
add(index,data);//给index位置添加data数据,其他数据会往后挤

注意:

  • 只能向前面有数据的位置插入,不能中间空了好多位置向后插入,比如容量大小为10,目前只有两个元素,则只能往0,1,2的位置插入,不能向3,4,5…等位置隔着插入
  • ArrayList对象不能存储基本类型,只能存储引用类型的数据。类似 不能写,但是存储基本数据类型对应的包装类型是可以的。所以,想要存储基本类型数据, <> 中的数据类型,必须转换后才能编写。

2.获取元素(get())

list.get(0);

源码

//get 
public E get(int index) {
        rangeCheck(index);
        return elementData(index);//如果上句不抛异常,则执行到这句,返回相应的元素
    }

//rangeCheck()
private void rangeCheck(int index) {
        if (index >= size)  //如果脚标大于size则抛异常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
//elementData()
E elementData(int index) {
        return (E) elementData[index];//返回数组相应脚标位置的元素
    }
//适配者模式把上面的取值过程转换为了一个方法名get()

大家注意一下上面的为什么要用index跟size比较而不用index跟elementData.length?(elementData就是我们存数据的数组)

答:size记录的是现在集合中的元素个数,而elementData.length获取到的是数组的长度。如果我指定初始容量为100,而只添加了两个元素。用elementData.length获取到的是100,但容量里面只有两个数据是有效的,所以存几个只能取几个。所以自己在写底层实现时也要有一个变量来记录数据的个数,因为初始数组大小不准。

如果是数组的话,获取没放过元素的位置,返回的是默认值。而arraylist的get方法就会报错

获取元素个数:size()方法(数组中获取长度是length,String获取长度是length())

3.修改元素(set())

返回值为修改之前的值

list.set(0,"first");//注意他修改后会返回原来的数据 

4.删除元素(remove())

按照下标删返回删除之前的元素;按照元素删除,返回布尔值。

list.remove(int index);//按下标删除,返回值是删之前的元素
list.remove(Object o);//按内容删,返回一个布尔值,删成功或删失败
//删除一个元素后,后面的元素统一前移

注意:如果元素为整数,remove的参数为int,按照下标删除;参数为Integer,按照元素删除

list.remove(3);//按脚标删
list.remove(new Integer(3));//按元素删,注意存入的基础数据类型元素默认是它的对象类型的

如果容器中有多个3,remove一次,删除的是第一个3

ArrayList的遍历

ArrayList<String> list = new ArrayList<>();//指定容器寸的内容必须是String的(泛型的一个特点,编译时就把类型确定了)
//1.8版本之前,后面的<>里面需要加类型,
//1.8版本之后 可以不用加,因为1.8版本之后加入了语法糖,可以进行类型推断
//泛型不支持基本数据类型只支持对象数据类型

list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
//遍历ArrayList
for(int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}
//迭代器:将Collection集合的遍历统一起来,迭代器一开始指向空null
        Iterator<String> iterator = list.iterator();//注意不是new
/*
        //判断是否有下一个元素,并没有获取它的值,而是他的内存地址,如果内存地址都没有,则值一定没有
        boolean hasNext = iterator.hasNext();
        //如果有,返回true,如果没有,返回false
        System.out.println(hasNext);
        //过去下一个元素
        iterator.next();
*/

 while (iterator.hasNext()) {
                String e = iterator.next();
     			// 删除当前迭代到的对象
     			//iterator.remove(); 输出还会是原来的那些元素,这里是为了不影响迭代的过程(如果删除了第一个元素,后面的元素会往前挤),但实际上这个元素确实被删了,不信你试试输出size()
                System.out.println(e);
            }

只有实现Itreable接口才可以使用增强for循环,他的底层就是用迭代器实现的

Iterator只需要记住hasNext()、next()、remove()即可
注:

  • 通过迭代器删除元素,不会影响整个遍历过程
  • 迭代完成后,迭代器的指针就会指向最后一个元素,所以要想再次遍历就需要重新过去迭代器对象

list集合的迭代器

ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
ListIterator<String> listIterator = list.listIterator();
//从上向下
 while (listIterator.hasNext()) {
            System.out.println(listIterator.next());
        }
//从下向上
while (listIterator.hasPrevious()) {
            System.out.println(listIterator.previous());
        }
//listIterator独有的方法
//添加元素
listIterator.add("e");
//获取上一个元素的下标
listIterator.previousIndex();
//获取下一个元素的下标
listIterator.nextIndex();

Iterator只能从上往下找(相当於单链表),ListIterator的特点是既能从上往下找,也能从下往上找。(相当于双向链表)

两个集合关系的方法

集合的一些方法

ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("bb");
        list.add("dd");
        list.add("ee");
        list.add("ff"); 
//根据元素获取下标,第一次出现时的下标,  -1表示该元素不存在
        System.out.println(list.indexOf("aa"));//输出0
        System.out.println(list.indexOf("h"));//输出-1
        System.out.println(list.indexOf("bb"));//输出1
        //最后一次出现的位置,比如获取某个人的最后一次登录时间
        System.out.println(list.lastIndexOf("bb"));//输出2
        //判断是否包含某个元素
        System.out.println(list.contains("a"));//其实index方法就包含了这个功能,他的底层实现用的就是indexOf
        //是否为空,即size()是不是为0。返回true为空
        list.isEmpty();
        //删除集合中的所有元素
        list.clear();

集合求子集,两集合求合集、差集、交集

ArrayList<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");

ArrayList<String> list2 = new ArrayList<>();
        list2.add("aa");
        list2.add("bb");
        list2.add("bb");
        list2.add("cc");
        list2.add("ff");
        list2.add("gg");
        /*
            子集
            第一个参数:起始下标(包含)
            第二个参数:结束下标(不包含)
         */
        List<String> list1 = list.subList(1,3);  //父类引用子类对象
        System.out.println(list1);
        //stulist不能单独使用,他是定义在ArrayList里的内部类(且他是私有的,只能由外部类使用),就比如有一个私有成员变量age,如果你都没这个对象了,那这个age也就不存在了

        /*
        //求合集  将参数集合中的所有元素都添加到调用方法的集合中
        //参数传的是集合,返回值是布尔型
        list.addAll(list2);
        System.out.println(list);
         */

        /*
        //求差集:将list中 list合list1的交集删除
        //参数传的是集合,返回值是布尔型
        //这里注意一下:如果list2里只有一个dd元素,而list里有多个dd元素,则list里所有的dd都会被删除
        list.removeAll(list2);
        System.out.println(list);
         */

        //求交集
        list.retainAll(list2);
        System.out.println(list);

subList源码

public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);//先检查范围是否正常
        return new SubList(this, 0, fromIndex, toIndex);//当上面不抛异常才返回
    }

static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }

数组和集合的转换

1.集合转数组

list.toArray(),他不灵活,返回值是Object

toArray有个可传参的方法,他是泛型方法,传进的参数是什么类型的它就返回什么类型<T> T[] toArray(T[] a);
eg:


        List<String>  list1 = new ArrayList<>();
        list1.add("a");
        list1.add("b");
        list1.add("c");
        Object[] objectArray  = list1.toArray();
 
        
        List<String> list2= new ArrayList<>();
        list2.add("A");
        list2.add("B");
        list2.add("C");
        String[] strArray= list2.toArray(new String[list2.size()]);

2.数组转集合

int[] array_int = {1,2,3};
//asList:如果参数为基本数据类型数组,将整个数组作为集合的一个元素。如果是对象类型的数组,将数组中的元素转换到集合中
List list = Arrays.asList(array_int); //将会输出数组首元素的内存地址

Integer[] array_integer = {1,2,3};//这些元素就是一个一个的Integer对象
Arrays.asList(array_integer);//这样就将元素一个一个的放进了集合中
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章