JavaSE基础八----<集合>List接口及其实现类,List接口的迭代

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE基础八----<集合>List接口及其实现类,List接口的迭代相关的知识,希望对你有一定的参考价值。


List接口及其实现类


List 接口继承了 Collection 接口以定义一个允许重复项的有序集合。

特点:可以存储重复数据,且为有序的(有序即按照元素的存储顺序进行排序).

List接口有三个实现类;
ArrayList,LinkedList,Vector.

List中继承了Collection的一些方法之外,自己也定义了一些方法;具体的方法将在List接口的实现类中进行学习.


1List接口的实现类(图解认识)


1.1ArrayList的认识(图解)


ArrayList底层为数组实现,查询快,增删慢

ArrayList实现了长度可变的数组,在内存中分配连续的空间;遍历元素和随机访问元素的效率比较高.


在第一次添加元素时;创建数组,会在底层创建默认长度为10的数组(自己可以更改);当数据内容满了之后,继续添加元素时,数组会自动地扩容一段原来长度的1.5倍的新数组.

效果图:

在这里插入图片描述
缺点:扩容后,如果元素不能存满时,空间浪费.

在增添元素时,如果是中间插入,那么后面的元素一个一个往后移动;在删除数组中的某个元素时,后面的元素依次往前移动,所以增删慢.


1.2LinkedList 的认识(图解)


LinkedList实现类,底层使用链表存储数据;LinkedList是以双链表存储的

查询慢,增删快.

在这里插入图片描述

效果图:
在一个节点中,数据存储在数据域中,头指针和尾指针;负责指向上一个节点和下一个节点的地址链接工作.

LinkedList图

那么,在元素查找时,从第一个节点开始向后查找,所以查找慢;
增加,删除元素时,只需要改变头指针尾指针的指向位置即可;其他元素不移动;增删快.


Vector实现类:
底层也是数组实现,默认容量为10;扩容速度为原来的2倍;添加了同步锁,是线程安全的.



2.List接口的实现类具体内容



在创建实现类对象时,可以使用多态的方式,即List接口的引用变量指向实现类的对象,但是这时,创建的变量只能调出List的方法以及List以上(例如Collection接口)的方法;而不能调出实现类里面的自己的方法;所以一般用子类的引用变量指向子类的创建对象.

2.1Arraylist


2.1(1)ArrayList的构造方法


在这里插入图片描述

默认调用无参的构造方法,创建ArrayList对象时,并没有创建数组;即空数组.

  • public ArrayList( )
//无参构造方法
ArrayList<String> a=new ArrayList<>();
//进入到ArrayList底层源码
//在没有添加任何元素时,这时实际上数组还没有创建;当填入数据元素时,默认创建长度为10的数组;
/*public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  }*/
//transient Object[] elementData; // non-private to simplify nested class access

有参构造方法

  • public ArrayList(Collection<? extends E> c)
    将另一个集合直接传入这个集合.
  • public ArrayList(int initialCapacity)
    创建指定长度的数组;
ArrayList<String> a=new ArrayList<>(3);//创建长度为3的数组

查看这个创建指定长度数组,构造方法源码;判断传入的参数,如果为0,仍然不创建数组,如果为负数,有抛出异常提示处理;
(注意:这个传入的参数有最大值限制,为整数的最大值减去8)
源码:private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

在这里插入图片描述



2.1(2)ArrayList中的常用方法


添加方法

  • 1.public boolean add(E e);
    add方法,默认向尾部添加元素;返回值为布尔值;
//例如,给定长度为5,一开始底层创建长度为5的数组;
ArrayList<String> a=new ArrayList<>(5);
//public boolean add(E e);add方法,默认向尾部添加元素;
a.add("a");a.add("b");a.add("c");a.add("d");a.add("e");
//这里先进行检测,通过grow()方法进行扩容;扩容一个长度为7的新数组;
a.add("f");a.add("g");
//这里先进行检测,通过grow()方法进行扩容;扩容一个长度为10的新数组;
a.add("h");a.add("i");a.add("j");
//这里先进行检测,通过grow()方法进行扩容;扩容一个长度为15的新数组;
a.add("k");a.add("l");
//虽然没有填充满元素,但是底层创建的长度为15的数组剩下的3个空间仍然存在
System.out.println(a);
//在这里输出的时候,看到的是size()方法统计的实际传入的元素个数;
//[a, b, c, d, e, f, g, h, i, j, k, l]

实际输出的数组长度为size( )统计的个数;
在这里插入图片描述

当数组中不足以存储元素时,调用grow方法进行扩容,新建一个原来数组的长度1.5倍的新数组;注意在grow方法中还用了Arrays中的copyOf方法进行原来数组的复制以及设定新数组长度.

在这里插入图片描述

在这里插入图片描述


  • 2.public void add(int index,E element)
    向指定的下标位置插入元素
ArrayList<String> a=new ArrayList<>();
a.add("0");a.add("q");a.add("b");a.add("d");
//向下标索引2位置插入x,这时原本位置的b和d向后移动
//public void add(int index,E element);
a.add(2,"x");
System.out.println(a);//[0, q, x, b, d]

删除方法

  • 1.public void clear()
    清空所有元素
  • 2.public boolean remove(Object o);
    从前向后,删除第一个出现的指定元素;
  • public E remove(int index)
    删除指定下标位置的元素,返回值为被删除的元素;

  • protected void removeRange(int fromIndex,int toIndex)
    受保护权限的方法;在子类中调用;
    删除某一段区间[fromIndex:开始下标位置,toIndex:结束下标位置(不包括)]的元素;
import java.util.ArrayList;
//写一个Demo02类继承ArrayList实现类
public class Demo02 extends ArrayList {
    public static void main(String[] args) {
        //新建Demo02类的对象
        Demo02 d2=new Demo02();
        d2.add("Q");//0(下标位置)
        d2.add("W");//1
        d2.add("E");//2
        d2.add("R");//3
        d2.add("G");//4
        //protected void removeRange(int fromIndex,int toIndex)
        //受保护权限的方法;在子类中调用;
        // 删除某一段区间[fromIndex:开始下标位置,toIndex:结束下标位置(不包括)]的元素;
        //删除0`3位置的元素;
        d2.removeRange(0,4);
        System.out.println(d2);//[G]
    }
}

判断方法

和Collection接口的类似

  • public boolean isEmpty();
    判断是否为空

  • public boolean contains(Object o)
    判断是否包含某个元素


查找方法
  • 1.public E get(int index)
    查找给定下标位置的元素,返回值为查找的元素;

为什么说它查找比较快;进入get方法源码;可以看到在查找前,首先进行一个检测;传入的下标是否在数组的长度范围内;由于底层是数组结构,可以精确地找到指定下标索引的元素

   public E get(int index) {
        rangeCheck(index);
        return elementData(index);
    }
  • 2.public int indexOf(Object o)
    从前向后,查找指定元素首次出现的位置;返回值为下标索引数;
  • 3.public int lastIndexOf(Object o);
    从后向前;找指定元素首次出现的位置;返回值为下标索引数;

替换方法
  • public E set(int index,E element)
    将指定下标位置的元素替换掉;返回为替换掉的元素;
    E:被替换的元素;index:下标索引;element:即将换入的元素;

截取方法
  • public java.util.List subList(int fromIndex,int toIndex)
    截取一段指定位置(formIndex:开始下标位置;toIndex:结束下标位置(不包括))的数组;返回一个新的数组;
//截取方法
        //public java.util.List<E> subList(int fromIndex,int toIndex)
        //截取一段指定位置(formIndex:开始下标位置;toIndex:结束下标位置(不包括))的数组;返回一个新的数组;
        ArrayList<String> a=new ArrayList<>();
        a.add("q");//0
        a.add("w");//1
        a.add("e");//2
        a.add("r");//3
        a.add("f");//4
        a.add("G");//5
        System.out.println(a.subList(0,5));//[q, w, e, r, f]
        //截取得到的是一个新的数组,原来的数组还在;
        System.out.println(a);//[q, w, e, r, f, G]

排序方法
  • public void sort(java.util.Comparator<? super E> c)
    按指定规则进行排序
    在Jdk1.8以后出现的方法
//排序方法;
//public void sort(java.util.Comparator<? super E> c)
ArrayList<String> a=new ArrayList<>();
a.add("5");
a.add("1");
a.add("4");
a.add("2");
System.out.println(a);//未排序之前 [5, 1, 4, 2]
a.sort(new Comparator<String>() {
    @Override
    //使用匿名内部类;定义排序规则
    public int compare(String o1, String o2) {
          return o1.compareTo(o2);
      }
  });
System.out.println(a);//排序后 [1, 2, 4, 5]

遍历方法
  • public void forEach(java.util.function.Consumer<? super E> action)
//遍历方法
//public void forEach(java.util.function.Consumer<? super E> action)
//以流的方式遍历,
ArrayList<String> arr=new ArrayList<>();
arr.add("Q");
arr.add("W");
arr.add("A");
//这里的q可用任意字母代替
arr.forEach((q)-> System.out.println(q));
//Q
//W
//A

涉及到两个集合的AII方法

  • 1.public boolean addAll(java.util.Collection<? extends E> c)
    把指定集合添加到这个集合;返回值为布尔值;

  • 2.public boolean containsAll(java.util.Collection<?> c)
    返回值为布尔值,作用是判断这个集合是否有另一个集合的元素;
    只要两个集合中有一个不相同元素;返回false;
    只要这个集合完全包含另一个集合的所有元素(另一个集合可以没有这个集合的元素);返回true;

  • 3.public boolean removeAll(java.util.Collection<?> c)
    返回值为布尔值;作用是删除两个集合的相同元素;
    如果两个集合中有相同的元素,返回true;
    如果两个集合中完全没有相同的元素;返回false;

  • 4.public boolean retainAll(java.util.Collection<?> c)
    返回值为布尔值,作用是保留两个集合中的相同元素;
    只要两个集合中有不同的元素,就返回true;
    如果两个集合中的元素完全相同;返回false;



2.2 LinkedList



LinkedList的构造方法

在这里插入图片描述
两个构造方法;

  • public LinkedList( )
    无参构造方法,创建一个空链表.
  • public LinkedList(@NotNull java.util.Collection<? extends E> c)
    有参构造方法,传入一个集合.

查看LinkedList的源码;底层是链表结构,每一段数据存到一个Node节点中,item存储数据;由于它的结构不像数组那样紧凑地排放在一块,所以需要prev和next来存储上一个节点和下一个节点的地址.

在这里插入图片描述
例如:

LinkedList<String> linkedList1=new LinkedList<>();
        linkedList1.add("a");
        linkedList1.add("b");
        linkedList1.add("c");
        linkedList1.add("d");

在这里插入图片描述



LinkedList的常用方法



对于它查询慢的原因,可以看看LinkedList的查找方法;

  • public E get(int index)
    虽然参数为index,但是这并不像数组中的下标排序,这里可理解为一种元素位置的底层序号.
    底层有这样一段代码.()类似于数组的折半查找方式 );它在查询数据的时候,要从头节点或者尾节点开始,一个一个去查找,查询效率变低.
 Node<E> node(int index) {
        // assert isElementIndex(index);
        //如果要找的位置序号在左边,从first头结点开始找,
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        //如果要找的位置序号在右边,从last尾节点开始找,    
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

返回值方法
booleanadd(E e) ;将指定的元素加到此列表的末尾。
voidadd(int index, E element) ;在此列表中的指定位置插入指定的元素。
booleanaddAll(Collection<? extends E> c) ;将指定集合中的所有元素加到到此列表的末尾。
booleanaddAll(int index, Collection<? extends E> c) ;将指定集合中的所有元素插入到此列表中,从指定的位置开始。
voidaddFirst(E e) ;把指定的元素e加到列表的头部。
voidaddLast(E e) 将指定的元素e加到此列表的后面;
voidclear( ) 删除列表的所有元素。
booleancontains(Object o) 如果此列表包含指定的元素,则返回 true 。
EgetFirst() 返回此列表中的第一个元素。
EgetLast() 返回此列表中的最后一个元素。
intindexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
intlastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
Eremove( ) ;检索并删除此列表的第一个元素。
Eremove(int index) ;删除该列表中指定位置的元素。
EremoveFirst( ) ;从此列表中删除并返回第一个元素。
EremoveLast( ) ;从此列表中删除并返回最后一个元素。
Epeek( ) ;检索但不删除此列表的第一个元素;返回它.
--------------------------------------------------------------------------------------------------------
Epop( ) ;从此列表表示的堆栈中弹出(删除)一个元素。 (出栈);后进先出.
voidpush(E e) 将元素压入到由此列表表示的堆栈上。(入栈)
--------------------------------------------------------------------------------------------------------
booleanoffer(E e) 将指定的元素添加为此列表的尾部(最后一个元素)。 (入队)先进先出
Epoll() 检索并删除此列表的头(第一个元素)。 (出队)


3.List接口的实现类迭代




以ArrayList实现类为例;这里写了一个集合为Alist;里面有5个元素,[Q,Q, Q, W, E, R]

通过一个方法进行迭代;

  • public java.util.stream.Stream stream();
    最终以流的方式输出.
//1.简单的方法遍历;
//public java.util.stream.Stream<E> stream();
Alist.stream().forEach((a)-> System.out.println(a));

for循环遍历

//2.for循环;
        for(int i=0;i<Alist.size();i++){
        //这里使用get方法获取元素;
            System.out.println(Alist.get(i));
        }

在遍历的时候,可以对集合中的元素进行删除操作,但是注意下标位置与遍历时的位置不一样了;可能会出错;

//在for循环时想要进行删除元素的操作;
        for(int i=0;i<Alist.size();i++){
            System.out.println(Alist.get(i));
            if(Alist.get(i).equals("Q")){
                Alist.remove("Q");
                i--;
            }
        }
        System.out.println(Alist);//[W, E, R]

增强的for循环遍历

//3.增强的for循环;
        for(String demo:Alist){

        }
        System.out.println(Alist);//[Q, Q, Q, W, E, R]

在增强for循环中,实际上是不能进行对元素的删除操作(删除操作时会出现异常),如果要删除,只能删除一个,必须用break停止操作.

 //增强for循环的删除操作;
        for(String demo:Alist){
            if(demo.equals("Q")){
                Alist.remove(demo);
                break;
            }
        }
        System.out.println(Alist);//[Q, Q, W, E, R]

迭代器遍历1

用到了迭代器;

  • iterator( )
//迭代器1
        //public java.util.Iterator<E> iterator()
        //首先获取迭代器对象;
        Iterator<String> iterator=Alist.iterator();
        //这里使用迭代器的hasNext方法,进行判断集合中还有没有下一个元素;
        //public abstract boolean hasNext()
        while (iterator.hasNext()){
            //如果有就使用迭代器的next方法向下移动指针,并且返回指针指向的元素;
            String s=iterator.next();
            System.out.println(s);
        }

在迭代器中可以进行删除元素的操作,使用迭代器的remove方法;删除元素;在删除后里面的游标(指针)会回退; 在迭代器中,cursor(指针)会记录遍历的元素序号.

        //public java.util.Iterator<E> iterator()
        Iterator<String> iterator=Alist.iterator();
        //public abstract boolean hasNext()
        while (iterator.hasNext()){
            String s=iterator.next();
            if(s.equals("Q")){
            //使用迭代器的remove方法;删除元素;在删除后里面的游标(指针)会回退
                iterator.remove();
            }
        }
        System.out.println(Alist);//[W, E, R]

迭代器遍历2

jdk1.8更新之后;在List接口中特有的迭代器;只适用于List接口的实现类.

  • ListIterator( )
返回值类型方法
voidadd(E e) ;将指定的元素插入列表(可选操作)。
booleanhasNext() ;返回 true如果遍历正向列表,列表迭代器有多个元素。
booleanhasPrevious() ;返回 true如果遍历反向列表,列表迭代器有多个元素。
Enext() ;返回列表中的下一个元素,并且前进光标位置。
intnextIndex() ;返回调用 next()返回的元素的索引。
Eprevious() ;返回列表中的上一个元素,并向后移动光标位置。
intpreviousIndex() ;返回由后续调用 previous()返回的元素的索引。
voidremove() ;从列表中删除由 next()或 previous()返回的最后一个元素(可选操作)。
voidset(E e) ;用指定的元素替换由 next()或 previous()返回的最后一个元素(可选操作)。


以上是关于JavaSE基础八----<集合>List接口及其实现类,List接口的迭代的主要内容,如果未能解决你的问题,请参考以下文章

JavaSE基础八----<集合>Set接口及其实现类,Set接口的迭代方式

JavaSE基础八----<集合>泛型集合体系Collection接口中的方法

JavaSE集合之List

java基础35 双例集合Map及其常用方法

JavaSE

JavaSE集合基础总览