首页 > 其他分享 >第十三章《集合》第2节:List集合

第十三章《集合》第2节:List集合

时间:2023-01-02 15:34:27浏览次数:54  
标签:13 ArrayList 元素 List 链表 第十三章 集合

​List这个单词意为“列表”,List类型的集合的特点是:元素呈线性排列,致密且有序。下面的图13-3展示了List类型集合的特点。

第十三章《集合》第2节:List集合_链表

图13-3 List类型集合​

图13-3中的每一个小圆形代表一个元素,可以看到,这些元素被放到List集合中后被排成一列,这就是“线性排列”。List集合中的元素都是连续的,中间不会空隙,这个特点可以概括为“致密”。List集合中每个元素都有自己的索引,因此它们有先后顺序,这个特点可以概括为“有序”。元素的索引就是元素的下标,程序员可以通过指定索引来找到List集合中的任意元素。​

List是Collection的子接口,因此它继承了Collection接口的全部方法。Collection所定义的方法如表13-1所示。​

表13-1 Collection接口的方法​

方法​

功能​

int size()​

计算集合中元素的数量​

boolean isEmpty()​

判断集合是否为空集合​

boolean contains(Object o)​

判断是否含有参数对象​

Iterator iterator()​

产生迭代器​

Object[] toArray()​

返回一个包含所有元素的对象数组​

boolean add(E e);​

向集合中加入对象,成功时返回true​

boolean remove(Object o)​

把参数对象移除到集合之外​

boolean containsAll(Collection c)​

判断c是否为当前集合的子集​

boolean addAll(Collection c)​

把c中的所有元素加入到集合末尾​

boolean removeAll(Collection c)​

清空指定集合​

boolean retainAll(Collection c)​

求当前集合与c中的共同元素​

void clear()​

清空集合​

List在继承了Collection方法的基础上,又扩展出很多通过索引操作元素的方法,下面的表13-2展示了List接口所定义的方法。​

表13-2 List接口的方法​

方法​

功能​

void add(int index, Object element)​

将元素element插入到List集合的index 处​

boolean addAll(int index,Collection c)​

将c所包含的所有元素都插入到集合的index处​

Object get(int index)​

返回集合index索引处的元素​

int indexOf(Object o)​

返回对象o在List集合中第一次出现的位置索引​

int lastIndexOf(Object o)​

返回对象o在List集合中最后一次出现的位置索引​

Object remove(int index)​

删除并返回index索引处的元素​

Object set(int index,Object element)​

将index索引处的元素替换成element,返回被替换的元素​

List subList(int fromIndex, int toIndex)​

返回从索引fromIndex(包含)到索引toIndex (不包含)处所有集合元素组成的子集合。​


13.2.1 ArrayList类和Vector类

ArrayList类和Vector类是List接口最典型的两个实现类,它们都具有Collection接口和List接口所定义的方法。ArrayList和Vector这两个类的实现原理相似,它们的内部都封装了一个动态的、允许再分配的Object数组。当向ArrayList或Vector中添加的元素个数超出了该数组的长度时,数组会被重新分配空间,以存放更多的元素,因此读者也可以理解为ArrayList和Vector中的数组会自动增加。ArrayList和Vector在创建对象时,可以通过参数指定数组的初始长度,例如下面的语句在创建对象时设置数组长度为20。​

ArrayList list = new ArrayList(20);​

如果没有指定数组的长度,虚拟机会在对象内部创建长度为10的数组。​

程序中对集合的操作一般包括:向集合中加入元素,获取集合中的某个元素、用一个对象取代集合中的某个元素、移除集合中的某个元素、清空集合等。下面的【例13_01】展示了ArrayList对元素的各种操作。​

【例13_01 List集合对元素的常用操作】

Exam13_01.java​

import java.util.ArrayList;
public class Exam13_01 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
//向集合中加入5个元素1、2、3、4、5
for(int i=1;i<=5;i++){
list.add(i);//①
}
System.out.println("加入5个元素后的集合:"+list);
//在索引为2的位置插入元素6
list.add(2,6);//②
System.out.println("在索引2处插入元素6之后的集合:"+list);
//移除集合中的元素3
list.remove(Integer.valueOf(3));//③
System.out.println("移除元素3之后的集合:"+list);
//用元素8取代索引为1的元素
list.set(1,8);
System.out.println("用元素8取代索引为1处元素后的集合:"+list);
//取出索引为3的元素
Object element = list.get(3);//④
System.out.println("索引为3的元素是:"+element);
//清空集合
list.clear();
System.out.println("清空后的集合:"+list);
}
}

集合中只能加入对象而不能加入基础类型数据,本例中直接向ArrayList中加入1、2、3、4、5并没有出现任何问题,是因为包装类具有自动装箱功能,因此实际上加入集合的是5个Integer类的对象。语句①中add()方法把元素加入集合,由于没有指定元素在集合中的位置,因此元素会被加入到集合的末尾,而语句②中的add()方法指定了元素在集合中的位置,因此元素会被加入到集合的特定位置,这个元素进入集合后,由于它占据了某个特定位置,原来该位置上的元素就会向后移动,因此这个操作实际上应该被称为“插入”。需要注意,插入元素的索引值不能超过集合的长度,例如集合中只有5个元素,就不能把新元素插入到集合索引为10的位置。语句③的作用是移除集合中的元素3,可以看到,在调用remove()方法时,为方法传递的参数是一个Integer对象而不是整数3,这是因为remove()方法有两个版本,一个版本用参数指定移除的对象,另一个版本用参数指定移除元素的索引,因此如果语句③在调用remove()方法时传递的参数是3,那么就表示移除索引为3的元素。语句④通过get()方法获得了一个特定索引的元素,集合中的元素本来是Integer,但语句中却用Object类的引用指向了所获取的元素,这是因为集合在存入元素时会丢失元素的类型,因此使用get()方法获取的元素都被看作Object类的对象。如果希望集合能够记住元素的真实类型,可以在创建集合对象时指定集合的泛型,例如:​

ArrayList<Integer> list = new ArrayList<Integer>();​

以上语句中所创建的ArrayList对象只能存放Integer类的对象,并且使用get()方法获取到的元素也会被虚拟机直接看作Integer类而不是Object类。【例13_01】虽然使用ArrayList对象为例进行演示,实际上如果把程序中的ArrayList替换为Vector,程序也不会出现语法问题并且会有相同的执行效果。【例13_01】的运行结果如图13-4所示。​

第十三章《集合》第2节:List集合_链表_02

图13-4【例13_01】运行结果​

程序中除对集合的元素进行各种操作之外,往往还会对集合做各种判断,这些判断包括:计算集合中元素的数量、判断集合是否为空集合、判断集合中是否含有某个对象、当前集合与其他集合是否有共同元素等。下面的【例13_02】展示了如何对List类型集合进行各种判断。​

【例13_02 判断List集合特性】

Exam13_02.java​

import java.util.ArrayList;
public class Exam13_02 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
System.out.println("list1是否是空集合:"+list1.isEmpty());
System.out.println("list1中的元素数量:"+list1.size());
System.out.println("list1中是否包含元素2:"+list1.contains(2));
//向list1中加入1、2、3、4、5
for(int i=1;i<=5;i++){
list1.add(i);
}
System.out.println("向list1加入5个元素后:");
System.out.println("list1是否是空集合:"+list1.isEmpty());
System.out.println("list1中的元素数量:"+list1.size());
System.out.println("list1中是否包含元素2:"+list1.contains(2));
//向list2中加入2、4、6、8、10
for(int i=1;i<=5;i++){
list2.add(i*2);
}
System.out.print("使用增强型for循环遍历list2:[");
for (Integer i:list2){
System.out.print(i+" ");
}
System.out.println("]");//打印右中括号并换行
System.out.println("list1与list2是否有共同元素:"+list1.retainAll(list2));
System.out.println("两个集合的共同元素是:"+list1);
}
}

【例13_02】的运行结果如图13-5所示。​

第十三章《集合》第2节:List集合_链表_03

图13-5【例13_02】运行结果​

从图13-5可以看出:增强型for循环不仅仅能够遍历数组,还能够遍历List类型的集合,此外还可以看到,调用list1的retainAll()方法后,list1会去掉list2所没有的那些元素,值保留两个集合的共同元素。​

【例13_02】的代码中,如果把ArrayList替换为Vector也会运行出同样的效果。Vector和ArrayList如此相像,它们之间又有什么区别呢?Vector是一个古老的集合,它从JDK 1.0就已经被定义,那时候Java还没有提供系统的集合框架。Vector中定义了一些名称很长的方法,例如addElement(),实际上这个方法与add ()方法没有任何区别。从JDK 1.2以后,Java提供了系统的集合框架,就将Vector改为实现List接口,成为了List接口的实现类之一,这导致 Vector里有一些功能重复的方法。Vector类的各种方法中,方法名更短的方法属于List接口中定义的方法,方法名更长的方法则是Vector原有的方法。​

ArrayList一开始就作为List的主要实现类,因此没有那些方法名很长的方法。除此之外,ArrayList和Vector的显著区别是:ArrayList 是线程不安全的,当多个线程访问同一个ArrayList集合时,如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该集合的同步性。但Vector集合则是线程安全的,无须程序保证该集合的同步性。因为Vector是线程安全的,所以Vector的性能比ArrayList 的性能要低。实际上,即使需要保证List集合线程安全,也同样不推荐使用Vector实现类。后面会介绍一个Collections工具类,它可以将一个ArrayList对象变成线程安全的。​

13.2.2 LinkedList类

ArrayList和Vector的内部都有一个数组,元素就存储在数组中。把元素存储在数组中最大的优势就是能够实现随机访问,也就是说只要指出元素的索引,虚拟机就能够一次性计算出元素的内存地址并对其进行访问。但如果删除或插入一个元素,基于数组的集合在性能方面就没有优势了,这是因为每当删除集合中的一个元素后,这个元素后面的其他元素都要逐个向前“移动”,以填补它所留下的空缺,这就有可能出现删除一个元素,却导致一万个元素移动的情况。插入元素也会导致相同的情况发生,只是其他元素变成了向后移动。​

除了基于数组的集合,Java语言还提供了一种基于链表的集合。链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,如图13-6所示。​

第十三章《集合》第2节:List集合_Java_04

图13-6链表示意图​

从图13-6可以看出:链表由一系列结点组成,每个结点包括两个部分:一部分是存储数据元素的数据域,另一部分是存储下其他结点地址的指针域。指针域中存储某个节点的地址这个行为常被表述为“指针指向某节点”。链表可以分为两种:如果一个节点只有一个指针,并且指针只是指向了下一个节点,那么这种链表称为“单链表”,而如果节点有两个指针,它们分别指向上一节点和下一节点,那么这种链表就称为“双链表”。从链表的结构可以看出:只有找到某个节点才能找到它的下一节点,因此链表结构并不具备“随机访问”的特性。但链表却能够很容易的实现节点的插入和删除的操作,因为每次插入或删除一个节点时,只需要改变某个节点指针的指向就可以。​

Java语言也提供了表示链表的类,这个类就是LinkedList,更进一步说LinkedList实现的是一个双向链表。LinkedList定义了很多用于操作双向链表的方法,下面的表13-3展示了LinkedList所定义的方法,这些方法中并不包括List接口中所定义的那些方法。​

表13-3 LinkedList类的方法​

方法​

功能​

void addFirst(E e)​

把指定的元素插入到链表最前端​

void addLast(E e)​

把指定的元素插入到链表最末端​

E element()​

获取链表中的第一个元素但不删除它​

E getFirst()​

获取链表中的第一个元素但不删除它​

E getLast()​

获取链表中的最后一个元素但不删除它​

boolean offer(E e)​

把指定元素加入到链表最末端​

boolean offerFirst(E e)​

把指定元素添加到链表最前端​

boolean offerLast(E e)​

把指定元素加入到链表最末端​

E peek()​

获取链表中第一个元素但不删除它​

E peekFirst()​

获取链表中第一个元素但不删除它​

E peekLast()​

获取链表中最后一个元素但不删除它​

E poll()​

获取并删除第一个元素​

E pollFirst()​

获取并删除第一个元素​

E pollLast()​

获取并删除最后一个元素​

E pop()​

获取并删除第一个元素​

void push(E e)​

把指定元素添加到链表最前端​

E removeFirst()​

删除链表中第一个元素​

E removeLast()​

删除链表中第一个元素​

从表13-3可以看出:LinkedList类中有很多功能重复的方法,这是因为LinkedList实现了Iterable、Collection、Deque、List、Queue等多个接口,这些接口中的方法存在重复现象,而LinkedList继承了这些方法从而导致自身的方法出现大量重复。虽然LinkedList类中所定义的方法很多,但这些方法并不难理解。​

关于LinkedList有一个很典型的例子:猴子选大王,题目是:有100只猴子围成一圈,按顺序每只猴子从1到100编号,并打算从中选出一个大王。经过协商,决定出选大王的规则是:从第一个开始循环报数,数到14的猴子出圈,最后剩下来的就是大王。实现这个计算的思路很简单,用一个LinkedList对象存放1~100的整数,用int型变量count表示猴子报的数,用pointer表示元素的索引,然后开始模拟报数的过程,如果没有报到14,count和pointer的值都自增,表示轮到下一只猴子报数。每次报到14时,移除索引为pointer的元素,由于这个元素被移除,pointer不需要自增即可表示下一个元素的索引,而count的值恢复为1,表示下一只猴子报的数重新回到1。需要注意:pointer的值在与链表长度相等时要重置为0,这样表示重新回到链表头部。实际上这个问题也可以用ArrayList解决,但由于LinkedList在删除元素时速度更快,因此使用LinkedList完成操作性能更好。下面的【例13_03】展示了如何使用LinkedList解决猴子选大王的问题。​

【例13_03 猴子选大王】

Exam13_03.java​

import java.util.LinkedList;
public class Exam13_03 {
public static void main(String[] args) {
LinkedList<Integer> monkeys = new LinkedList<Integer>();
for (int i = 1; i <= 100; i++) {
monkeys.add(i);
}
int count = 1;//猴子报的数
int pointer = 0; //pointer的值就是当前被判断元素的索引
while (monkeys.size() > 1) {
if (count == 14) {
count = 1;
//移除该元素,由于它被移除,pointer不需要自增即可表示下一元素的索引
monkeys.remove(pointer);
}else{
count++;
pointer++;
}
if (pointer ==monkeys.size()) {//达到尾部,重新返回初始指针处
pointer = 0;
}
}
System.out.println("大王的编号是:"+monkeys.get(0));
}
}

【例13_03】的运行结果如图13-7所示。​

第十三章《集合》第2节:List集合_链表_05

图13-7【例13_03】运行结果​


13.2.3长度固定的List

第11章中讲解过一个操作数组的工具类:Arrays,这个类里提供了asList()方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个 List集合的类型既不是ArrayList,也不是Vector,而是Arrays.ArrayList,也就是说它的类型是Arrays的内部类ArrayList。​

Arrays.ArrayList是一个固定长度的List集合,因此只能遍历访问该集合里的元素,不可增加、删除该集合中的元素。任何试图增加或删除该集合元素的操作都会导致程序出现异常。下面的【例13_04】展示了如何获得长度固定的List集合,并通过forEach()方法遍历集合。​

【例13_04长度固定的List】

Exam13_04.java​

import java.util.*;
public class Exam13_04 {
public static void main(String[] args) {
List list = Arrays.asList("Java ","Python ","C++ ");
//获得list的真实类型
System.out.println("list的真实类型:"+list.getClass().getName());
System.out.print("中的所有元素list:");
//调用forEach()方法遍历list
list.forEach(System.out::print);
}
}

【例13_04】通过asList()方法获得了一个长度固定的List对象,并通过getClass()方法获得对象的真实类型,紧接着调用forEach()方法遍历集合。forEach()方法是Iterable接口中定义的一个方法,因为List接口是间接Iterable子接口,因此所有List类型的集合都拥有forEach()方法。forEach()方法用来遍历集合,方法的参数类型Consumer,Consumer是一个接口,其中定义的accept()抽象方法用来表示如何访问集合元素。由于Consumer是一个函数式接口,因此本例中采用Lambda表达式表示其匿名实现类对象。【例13_04】的运行结果如图13-8所示。​

第十三章《集合》第2节:List集合_链表_06

图13-8【例13_04】运行结果

本文字版教程还配有更详细的视频讲解,小伙伴们可以点击这里观看。

标签:13,ArrayList,元素,List,链表,第十三章,集合
From: https://blog.51cto.com/mugexuetang/5983733

相关文章

  • 第十三章《集合》第3节:Set集合
    ​Set也是Collection的子接口,它定义了另一种形式的集合,专业上称之为Set集合。Set集合的特点如图13-9所示。图13-9Set类型集合​从图13-9可以看出:Set类型的集合就像是一个装......
  • Java Map 集合类简介
    java.util中的集合类包含Java中某些最常用的类。最常用的集合类是List和Map。List的具体实现包括ArrayList和Vector,它们是可变大小的列表,比较适合构建、存储和......
  • 第十三章《集合》第1节:Java集合概述
    ​实际开发过程中,常常需要集中存放多个数据,虽然程序员可以使用数组来保存多个对象,但数组长度不可变化,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的,如果需要保......
  • Vue案例——todolist
    最近在学习vue,实现todolist案例,实现效果如下:   该案例分为四个部分:header为输入框,body为列表,item是列表中的条目,footer为最下方的统计。实现步骤:①创建项目 v......
  • C# List<T> 转 DataTable
    网上有不少List<T>转DataTable的代码,但都是要指定类型的,如: ListToDT<T>(List<T>xxxx)这样的,这样用总觉得有点别扭,想实现如List<T>item.toDataTable()这样的转换......
  • C#.NET 随机排序集合(列表\数组) | 打乱集合(列表\数组)
    直接上代码:///<summary>///重排列表(打乱列表)///</summary>///<paramname="arr"></param>publicstaticList<string>ConfusionArray(List<string>list){......
  • winform控件缩写 (集合)
    winform控件缩写(集合)文章内所有的控件都是博主从网上辛苦整理出来的,希望对大家有用winform控件缩写(一)标准控件winform控件缩写(二)容器控件winform控件缩写(三)菜单和工具栏wi......
  • List接口实现类的遍历方式
    List接口实现类主要有:ArrayList,LinkedList,Vector【三者区别,可以看看java集合.xmind文件】一.ArrayList类的遍历:1publicclassListBianLiTest{2publics......
  • JMS与Spring之二(用message listener container异步收发消息)
    本文主要参考JavaMessageService(2nd)byMarcRicbard。在Spring框架中使用JMS传递消息有两种方式:JMStemplate和messagelistenercontainer,前者用于同步收发消息,后者用于......
  • Python操作Excel、Word、PPT、PDF、复杂文件、通信软件(微信、邮件、飞书、钉钉)、图片
    1、在这几年,我写了本书!很多朋友为了学习如何用Python自动化地处理各种文档,应该都下载过这个文档:python操作excel、word、pdf.pdf。时至今日,仅仅在CSDN一个渠道的下载量便超......