Java开发笔记(六十七)清单:ArrayList和LinkedList
前面介绍了集合与映射两类容器,它们的共同特点是每个元素都是唯一的,并且采用二叉树方式的类型还自带有序性。然而这两个特点也存在弊端:其一,为啥内部元素必须是唯一的呢?像手机店卖出了两部Mate20,虽然这两部手机一模一样,但理应保存两条销售记录才是。其二,不管是哈希类型还是二叉类型,居然都不允许按照加入时间的先后排序,要知道现实生活中不乏各种先来后到的业务场景。为了更方便地应对真实场景中的各类需求,Java又设计了清单List这么一种容器,用来处理集合与映射所不支持的业务功能。
提到清单,脑海里顿时浮现出从上往下排列的一组表格,例如购物清单、愿望清单、待办事项等等,它们的共同点一是都有序号,二是按线性排列。清单里的元素允许重复加入,并且根据入伙的时间顺序先后罗列,这些特征决定了清单是种贴近日常生活的简易容器。不过Java中的List属于接口,实际开发用到的是它的一个实现类ArrayList(列表,又称动态数组)。在某种程度上,列表的确跟数组很像,比如二者的内部元素都分配了整数序号/下标、都支持通过序号/下标来访问指定位置的元素等等。但列表贵为容器中的一员,自然拥有几点数组所不能比拟的优势,包括但不限于:
一、列表允许动态添加新元素,不管调用多少次add方法,也不必担心列表空间不够用的问题。下面代码便演示了如何声明列表实例并对其依次添加元素:
- // 创建一个列表(动态数组),其元素为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
而数组的大小一经初始化设定就不可调整,除非另外给它分配新的数组空间;
二、数组只能对指定位置的元素进行修改操作,列表不但支持修改指定位置的元素(set方法),还支持在指定位置插入新元素(add方法),或者移除指定位置的元素(remove方法)。
三、数组只有两种遍历方式:按下标遍历、通过简化的for循环遍历。而列表支持多达四种的遍历方式,分别说明如下:
1、简化的for循环。该方式同样适用于数组和容器,具体的遍历代码示例如下:
- // 第一种遍历方式:简化的for循环同样适用于数组和容器
- for (MobilePhone for_item : list) {
- System.out.println(String.format("for_item:%s %d",
- for_item.getBrand(), for_item.getPrice()));
- }
2、迭代器遍历。该方式与利用迭代器遍历集合是一样,都要先获得当前容器的迭代器,然后依次调用迭代器的next逐个获取元素。利用迭代器遍历列表的代码如下所示:
- // 第一种遍历方式:简化的for循环同样适用于数组和容器
- for (MobilePhone for_item : list) {
- System.out.println(String.format("for_item:%s %d",
- for_item.getBrand(), for_item.getPrice()));
- }
3、索引遍历。这里的索引是以0开始的序号,对应于数组的下标,只不过列表通过get方法获取指定位置的元素,而数组通过方括号引用某个下标。下面是使用索引遍历列表的代码例子:
- // 第三种遍历方式:与数组通过下标访问相似,列表通过索引获取指定位置的元素
- 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()));
- }
4、forEach遍历。Java8之后,每种容器都支持联合应用forEach与Lambda表达式的遍历方式,该方式的遍历代码见下:
- // 第四种遍历方式:使用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相同,并基于它的三个祖宗分别进行了下列方法拓展:
1、在清单List的功能增强方面,补充了如下的扩展方法:
addFirst:添加到清单开头
addLast:添加到清单末尾
removeFirst:删除清单开头的元素
removeLast:删除清单末尾的元素
getFirst:获取清单开头的元素
getLast:获取清单末尾的元素
2、在队列Queue的功能实现方面,提供了如下的队列方法:
offer:添加到队列末尾
offerFirst:添加到队列开头
offerLast:添加到队列末尾
peek:获取队列开头的元素
peekFirst:获取队列开头的元素
peekLast:获取队列末尾的元素
poll:删除队列开头的元素
pollFirst:删除队列开头的元素
pollLast:删除队列末尾的元素
3、在栈Stack的功能模拟方面,添加了如下的额外方法:
pop:队列开头的元素出栈,相当于方法removeFirst和pollFirst
push:新元素入栈,相当于方法addFirst和offerFirst
总的来说,链表的数据存储兼顾清单和队列的组织结构,常用于对数据进出有特殊要求的场合,例如采取先进先出FIFO的队列操作,以及采取先进后出FILO的栈操作。
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(六十七)清单:ArrayList和LinkedList的更多相关文章
- Java开发笔记(十七)各得其所的多路分支
前面提到条件语句的标准格式为“if (条件) { /* 条件成立时的操作代码 */ } else { /* 条件不成立时的操作代码 */ }”,乍看之下仿佛只有两个分支,一个是条件成立时的分支,另一个 ...
- Java开发笔记(序)章节目录
现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...
- Java开发笔记(六十九)泛型类的定义及其运用
前面从泛型方法的用法介绍到了泛型的起源,既然单个方法允许拥有泛化的参数类型,那么一个类也应当支持类级别的泛化类型,例如各种容器类型ArrayList.HashMap等等.一旦某个类的定义代码在类名称后 ...
- Java开发笔记(七十六)如何预防异常的产生
每个程序员都希望自己的程序稳定运行,不要隔三岔五出什么差错,可是程序运行时冒出来的各种异常着实烦人,令人不胜其扰.虽然可以在代码中补上try/catch语句捕捉异常,但毕竟属于事后的补救措施.与其后知 ...
- Java开发笔记(七十七)使用Optional规避空指针异常
前面在介绍清单用法的时候,讲到了既能使用for循环遍历清单,也能通过stream流式加工清单.譬如从一个苹果清单中挑选出红苹果清单,采取for循环和流式处理都可以实现.下面是通过for循环挑出红苹果清 ...
- Java开发笔记(六十五)集合:HashSet和TreeSet
对于相同类型的一组数据,虽然Java已经提供了数组加以表达,但是数组的结构实在太简单了,第一它无法直接添加新元素,第二它只能按照线性排列,故而数组用于基本的操作倒还凑合,若要用于复杂的处理就无法胜任了 ...
- Java开发笔记(六十八)从泛型方法探究泛型的起源
前面介绍各种容器之时,通过在容器名称后面添加包裹数据类型的一对尖括号,表示该容器存放的是哪种类型的元素.这样一来总算把Java当中的各类括号都凑齐了,例如包裹一段代码的花括号.指定数组元素下标的方括号 ...
- Java开发笔记(九十六)线程的基本用法
每启动一个程序,操作系统的内存中通常会驻留该程序的一个进程,进程包含了程序的完整代码逻辑.一旦程序退出,进程也就随之结束:反之,一旦强行结束进程,程序也会跟着退出.普通的程序代码是从上往下执行的,遇到 ...
- Java开发笔记(一百三十六)JavaFX的窗格
虽然Java自诞生之初就推出了AWT,紧接着第二版又推出升级后的Swing,打算在桌面开发这块大展拳脚:可是后来Java在服务器开发上大放异彩,在桌面开发上反而停滞不前,可谓失之J2SE收之J2EE. ...
随机推荐
- python面向对象的知识梳理
面向对象(Object Oriented Programming) 三个基本特征: 1.封装:包含两个概念,对象将变量(状态)和方法(用来改变状态或执行涉及状态的计算)集中在一个地方—即对象本身. 通 ...
- 基于docker搭建开源扫描器——伏羲
基于docker搭建开源扫描器——伏羲 1.简介 项目地址 伏羲是一款开源的安全检测工具,适用于中小型企业对企业内部进行安全检测和资产统计. 功能一览: 基于插件的漏洞扫描功能(类似于巡风) 漏洞管理 ...
- 【转载】CSS3 文字溶解效果
代码如下: <!DOCTYPE html> <html > <head> <meta charset="UTF-8"> <ti ...
- idea 启动调试模式总提示端口58346被占用问题
在开发的时候,莫名其妙没法用jrebel调试模式启动了tomcat了,最开始以为是后台端口占用问题,然而把后台java程序全部关了都没用.仔细排查,根据提示发现是端口58346被占用了, 于是便在 w ...
- [Swift]LeetCode3. 无重复字符的最长子串 | Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters. Examples: Giv ...
- mongo 联表查询
查询语句 db.getCollection("A表").aggregate([ { $lookup:{ from:"B表", localField:" ...
- github pages代码高亮highlighter
github pages 一直想添加代码高亮 highlighter ,基于 jekyll 3.0 的 rouge 终于搞定了: 下载代码高亮库 在 cmd 中输入: rougify style mo ...
- pytorch: 准备、训练和测试自己的图片数据
大部分的pytorch入门教程,都是使用torchvision里面的数据进行训练和测试.如果我们是自己的图片数据,又该怎么做呢? 一.我的数据 我在学习的时候,使用的是fashion-mnist.这个 ...
- Docker 下载镜像
文章首发个人网站: https://www.exception.site/docker/docker-pull-image 本文中,我们将需要学习 Docker 如何下载镜像? 一.前言 大家都知道, ...
- 用了 HTTPS 还不安全,问题就出在低版本 TLS 上
HTTPS 加密时代已经来临,近两年,Google.Baidu.Facebook 等互联网巨头,不谋而合地开始大力推行 HTTPS, 2018 年 7 月 25 日,Chrome 68 上线,所有 H ...