Java开发笔记(六十七)清单:ArrayList和LinkedList

前面介绍了集合与映射两类容器,它们的共同特色是每一个元素都是惟一的,而且采用二叉树方式的类型还自带有序性。然而这两个特色也存在弊端:其一,为啥内部元素必须是惟一的呢?像手机店卖出了两部Mate20,虽然这两部手机如出一辙,但理应保存两条销售记录才是。其二,不论是哈希类型仍是二叉类型,竟然都不容许按照加入时间的前后排序,要知道现实生活中不乏各类先来后到的业务场景。为了更方便地应对真实场景中的各种需求,Java又设计了清单List这么一种容器,用来处理集合与映射所不支持的业务功能。
提到清单,脑海里顿时浮现出从上往下排列的一组表格,例如购物清单、愿望清单、待办事项等等,它们的共同点一是都有序号,二是按线性排列。清单里的元素容许重复加入,而且根据入伙的时间顺序前后罗列,这些特征决定了清单是种贴近平常生活的简易容器。不过Java中的List属于接口,实际开发用到的是它的一个实现类ArrayList(列表,又称动态数组)。在某种程度上,列表的确跟数组很像,好比两者的内部元素都分配了整数序号/下标、都支持经过序号/下标来访问指定位置的元素等等。但列表贵为容器中的一员,天然拥有几点数组所不能比拟的优点,包括但不限于:html


1、列表容许动态添加新元素,无论调用多少次add方法,也没必要担忧列表空间不够用的问题。下面代码便演示了如何声明列表实例并对其依次添加元素:java

		// 建立一个列表(动态数组),其元素为MobilePhone类型
		ArrayList<MobilePhone> list = new ArrayList<MobilePhone>();
		list.add(new MobilePhone("华为", 5000)); // 第一个添加的元素,默认分配序号为0
		list.add(new MobilePhone("小米", 2000)); // 第二个添加的元素,默认分配序号为1
		list.add(new MobilePhone("OPPO", 4000)); // 第三个添加的元素,默认分配序号为2
		list.add(new MobilePhone("vivo", 1000)); // 第四个添加的元素,默认分配序号为3
		list.add(new MobilePhone("vivo", 1000)); // 第五个添加的元素,默认分配序号为4

 

而数组的大小一经初始化设定就不可调整,除非另外给它分配新的数组空间;数组


2、数组只能对指定位置的元素进行修改操做,列表不但支持修改指定位置的元素(set方法),还支持在指定位置插入新元素(add方法),或者移除指定位置的元素(remove方法)。数据结构


3、数组只有两种遍历方式:按下标遍历、经过简化的for循环遍历。而列表支持多达四种的遍历方式,分别说明以下:
一、简化的for循环。该方式一样适用于数组和容器,具体的遍历代码示例以下:设计

		// 第一种遍历方式:简化的for循环一样适用于数组和容器
		for (MobilePhone for_item : list) {
			System.out.println(String.format("for_item:%s %d",
					for_item.getBrand(), for_item.getPrice()));
		}

 

二、迭代器遍历。该方式与利用迭代器遍历集合是同样,都要先得到当前容器的迭代器,而后依次调用迭代器的next逐个获取元素。利用迭代器遍历列表的代码以下所示:orm

		// 第一种遍历方式:简化的for循环一样适用于数组和容器
		for (MobilePhone for_item : list) {
			System.out.println(String.format("for_item:%s %d",
					for_item.getBrand(), for_item.getPrice()));
		}

 

三、索引遍历。这里的索引是以0开始的序号,对应于数组的下标,只不过列表经过get方法获取指定位置的元素,而数组经过方括号引用某个下标。下面是使用索引遍历列表的代码例子:htm

		// 第三种遍历方式:与数组经过下标访问类似,列表经过索引获取指定位置的元素
		for (int i = 0; i < list.size(); i++) {
			MobilePhone index_item = list.get(i);
			System.out.println(String.format("index_item:%s %d",
					index_item.getBrand(), index_item.getPrice()));
		}

 

四、forEach遍历。Java8以后,每种容器都支持联合应用forEach与Lambda表达式的遍历方式,该方式的遍历代码见下:blog

		// 第四种遍历方式:使用forEach方法夹带Lambda表达式进行遍历
		list.forEach(each_item -> System.out.println(String.format(
				"each_item:%s %d", each_item.getBrand(), each_item.getPrice())));

  

尽管列表对于大多数的业务场景来讲够用了,但是仍旧没法知足部分特定的业务需求,由于ArrayList默认把新元素添加到列表末尾,也不存在默认的删除操做。而在计算机科学常见的数据结构当中,至少还有两种是列表所不能实现的,其中一个叫作队列Deque,另外一个叫作栈Stack。
队列取材于生活中的排队场景,譬如春运期间你们在火车站排队买车票,虽然有个别人嚷嚷着“我要插队”且自顾自地插了进去,也有人忍受不了漫长的等待而中途放弃排队改成骑单车回家,但多数人都会循规蹈矩地从队尾开始排队,买了票以后从队首离队。因而排队业务就抽象成为这么一种队列结构:添加时默认往末尾添加,删除时默认从开头删除。
至于栈则取材于计算机系统的寄存器操做,栈的特色是里面保存的数据为先进后出(同时也是后进先出),即最先添加的元素会被最后移除、最晚添加的元素会被最早移除。基于栈具备的数据先进后出特性,它经常使用于保存中断时的断点、保存子程序调用后的返回点、保存CPU的现场数据、在程序间传递参数等等。就栈做为一种容器的角色而言,每次添加的元素会默认加到开头,且每次删除操做会默认删去开头的元素,从而实现后进先出/先进后出的机制。
然而不论是队列仍是栈,它们的存储形式都如同清单那样线性排列,区别在于数据进出的默认方位。所以Java把队列、栈以及清单三者加以融合,推出了链表LinkedList(又称双端队列)这种数据结构,它一块儿实现了List与Deque接口,并在某种程度上模拟了栈的功能,从而变成专治各类不服的万能清单。
做为清单你们族的一员,链表LinkedList的基本用法与列表ArrayList相同,并基于它的三个祖宗分别进行了下列方法拓展:
一、在清单List的功能加强方面,补充了以下的扩展方法:
addFirst:添加到清单开头
addLast:添加到清单末尾
removeFirst:删除清单开头的元素
removeLast:删除清单末尾的元素
getFirst:获取清单开头的元素
getLast:获取清单末尾的元素
二、在队列Queue的功能实现方面,提供了以下的队列方法:
offer:添加到队列末尾
offerFirst:添加到队列开头
offerLast:添加到队列末尾
peek:获取队列开头的元素
peekFirst:获取队列开头的元素
peekLast:获取队列末尾的元素
poll:删除队列开头的元素
pollFirst:删除队列开头的元素
pollLast:删除队列末尾的元素
三、在栈Stack的功能模拟方面,添加了以下的额外方法:
pop:队列开头的元素出栈,至关于方法removeFirst和pollFirst
push:新元素入栈,至关于方法addFirst和offerFirst
总的来讲,链表的数据存储兼顾清单和队列的组织结构,经常使用于对数据进出有特殊要求的场合,例如采起先进先出FIFO的队列操做,以及采起先进后出FILO的栈操做。排序



更多Java技术文章参见《Java开发笔记(序)章节目录索引

相关文章
相关标签/搜索