java的容器与迭代器是一个老生常谈的话题了。

本文旨在与大家分享一些关于双向链表与迭代器的运用小技巧,并希望本篇文章的内容能够在项目中给你带来帮助。

Stack与LinkedList

Stack是一个LIFO(后进先出)的容器。若要在java中定义一个Stack应该怎么办?

也许你马上会想到,java中对Stack类型的容器提供了源生支持,所以我们使用jdk包中提供的Stack类不就解决问题了吗?

是的,这是很合理的思路。重复造轮子不是java的风格。那么就让我们来看看源生的Stack容器应该如何使用。

先来一睹jdk中的源生Stack容器类:

* @author Jonathan Payne * @since JDK1.0 */publicclass Stack extends Vector { /** * Creates an empty Stack. */ public Stack() { } /** * Pushes an item onto the top of this stack. This has exactly * the same effect as: * 
 * addElement(item)

* * @param item the item to be pushed onto this stack. * @return the item argument. * @see java.util.Vector#addElement */ public E push(E item) { addElement(item); return item; } ....... ..... ... .

嗯?等等,Stack继承自Vector?!

糟了!我们仅仅想要一个单纯的LIFO容器,而大名鼎鼎的Vector不仅速度慢还带有一堆我们不需要的“特性”,继续使用源生的Stack显然不是一个好的选择。

这下可棘手了,我们现在要如何实现一个Stack呢?

LinkedList

LinkedList是一个双向链表。关于它,想必不用介绍太多,光看名字就应该能够猜到,你想要的数据结构它应该都能实现。

所以,我们是不是可以通过LinkedList来实现一个自己的Stack类呢?

import java.util.LinkedList;public class Stack {  // 容器 private LinkedList lt = new LinkedList();  // 模拟栈的push方法 public void push(T e) { // 压栈 lt.addLast(e); } // 模拟栈的pop方法 public T pop() { // 弹栈 return lt.removeLast(); } // 模拟栈的peek方法 public T peek() { // 取得栈顶元素 return lt.getLast(); } public boolean isEmpty() { return lt.isEmpty(); } public static void main(String[] args) { Stack sk = new Stack(); sk.push("hello"); sk.push("world"); System.out.println(sk.pop()); System.out.println(sk.pop()); }}---------------------worldhello

太好了。通过LinkedList,我们模拟了一个LIFO数据结构的实现,并且这个类的名字也叫做Stack。

除此之外他还没有Vector的一大堆“特性”。这就是我们需要的单纯的LIFO容器。

迭代器

在上一小节,我们实现了我们自己的LIFO容器。在这一小节,我们想办法让这个LIFO容器变得更“完美”一些。

在Java中,任何容器都属于可迭代对象,且能被foreach所迭代。

显然,我们创造的Stack容器目前还未拥有迭代器特征。由于追求完美和代码洁癖是一个合格的程序员所应该具有的素养,所以接下来让我们对这个Stack进行一点小小的改造。

import java.util.Iterator;import java.util.LinkedList;// 继承Iterable接口,使其成为可迭代对象。public class Stack implements Iterable {  // 容器 private LinkedList lt = new LinkedList();  // 模拟栈的push方法 public void push(T e) { // 压栈 lt.addLast(e); } // 模拟栈的pop方法 public T pop() { // 弹栈 return lt.removeLast(); } // 模拟栈的peek方法 public T peek() { // 取得栈顶元素 return lt.getLast(); } public boolean isEmpty() { return lt.isEmpty(); }  // 可迭代对象的标准迭代方法 @Override public Iterator iterator() { return lt.iterator(); } public static void main(String[] args) { Stack sk = new Stack(); sk.push("hello"); sk.push("world"); // 通过foreach迭代对象(内部通过获取迭代器进行迭代) for (String s : sk) { System.out.println(s); }  // 显示的通过获取Stack迭代器进行迭代 Iterator skit = sk.iterator(); while (skit.hasNext()) { System.out.println(skit.next()); }  System.out.println(sk.pop()); System.out.println(sk.pop()); }}---------------------helloworldhelloworldworldhello

现在,Stack是一个标准的LIFO容器了。他就像其他的源生java容器一样,是一个可迭代对象并且能够被foreach所迭代。任何一个Java程序员,都能够像使用其他源生容器一样使用我们的自定义Stack容器了!

反向迭代

好景不长。

正当你在项目中愉快的使用上面的Stack容器解决一个又一个需求时,难题出现了。

业务方提出了一个讨人厌的需求,它需要反向遍历Stack容器。而追求严谨优雅的你,绝对不会允许使用for循环去遍历容器的这种low逼方式出现。

看来只好再对Stack容器的功能进行一些增强了。

import java.util.Iterator;import java.util.LinkedList;// 继承Iterable接口,使其成为可迭代对象。public class Stack implements Iterable {  // 容器 private LinkedList lt = new LinkedList();  // 模拟栈的push方法 public void push(T e) { // 压栈 lt.addLast(e); } // 模拟栈的pop方法 public T pop() { // 弹栈 return lt.removeLast(); } // 模拟栈的peek方法 public T peek() { // 取得栈顶元素 return lt.getLast(); } public boolean isEmpty() { return lt.isEmpty(); } // 可迭代对象的标准迭代方法 @Override public Iterator iterator() { return lt.iterator(); } // 返回一个可迭代对象。重写可迭代对象的iterator方法,返回重写了next()方法的迭代器对象。 public Iterable reversed() { return new Iterable() { public Iterator iterator() { return new Iterator() { private int current = lt.size() - 1;  // 实现hasNext方法 @Override public boolean hasNext() { return current >= 0; }  // 实现next方法,实现反向迭代 @Override public T next() { if (!hasNext()) { return null; } // 先输出结果再-- T element = lt.get(current--); return element; }  // 实现remove方法。remove掉最新迭代出的对象。(与源生容器的迭代器实现保持一致) @Override public void remove() { lt.remove(current + 1); } }; } }; } public static void main(String[] args) { Stack sk = new Stack(); sk.push("hello"); sk.push("world"); for (String s : sk) { System.out.println(s); }  Iterator skit = sk.iterator(); while (skit.hasNext()) { System.out.println(skit.next()); }  // 通过foreach反向迭代sk for (String s : sk.reversed()) { System.out.println(s); } // 显示的调用反向迭代器反向迭代sk Iterator reversedSkit = sk.reversed().iterator(); while (reversedSkit.hasNext()) { System.out.println(reversedSkit.next()); reversedSkit.remove(); }  if (!sk.isEmpty()) { System.out.println(sk.pop()); System.out.println(sk.pop()); } else { System.out.println("容器为空"); } }}---------------------helloworldhelloworldworldhelloworldhello容器为空

现在的Stack容器不仅是一个可迭代对象。通过调用reversed()方法还能支持反向迭代。利用这个容器不仅能解决问题,还能让解决问题的方式变得更优雅。真棒!

总结

大多数情况下,我认为都应该使用LinkedList来实现Stack。同理LinkedList也能够用来实现Queue。不过,需要注意的是通过这种方法实现的容器,依然和java中其他容器一样,默认情况下在并发状态中是不安全的。

并且,对于自己实现的容器,尽量通过迭代器设计模式对其进行功能增强,以符合java Collection的标准,并满足项目中的需求。

java 容器_我也来聊聊,JAVA容器与迭代器相关推荐

  1. 容器云java开发_使用码云构建 Docker 容器镜像并部署到华为云

    华为公有云平台的容器镜像服务开放了对码云代码库的支持. 华为云平台的容器镜像服务(SWR),能够支持从源码到镜像.从镜像到应用的容器镜像全生命周期的管理服务,为用户提供简单易用.安全可靠的镜像管理功能 ...

  2. java 注解_通俗易懂的讲解下Java注解

    对于Java注解,我咨询过一些身边的人,很多人表示: 知道怎么用,不熟悉 不知道你是不是这样?在我没有系统性的学习一边注解的时候,我也是如此,在我花时间学习过注解之后,我觉得,对于注解,最重要的在于理 ...

  3. spring 加载java类_在Spring中基于Java类进行配置的完整步骤

    在Spring中基于Java类进行配置的完整步骤 发布于 2020-7-7| 复制链接 基于Java配置选项,可以编写大多数的Spring不用配置XML,下面 前言JavaConfig 原来是 Spr ...

  4. java书籍_非科班,自学java需要把软件工程的课程全部学习完吗?

    问题一:非科班是否能自学Java.问题二:自学Java是否需要把软件工程课程全部学完?问题三:如何自学Java? 解决问题一:非科班是否能自学Java.不知道你是否有这个担心疑虑,从事Java技术开发 ...

  5. java组件_三个必不可少的Java平台组件:什么是JVM,JDK,JRE?有啥区别?

    刚接触Java的开发人员经常想知道Java虚拟机,Java开发工具包和Java运行时环境与众不同的地方.他们也很好奇这三个Java平台组件如何在Java应用程序中一起工作.最后,开发人员需要知道他们将 ...

  6. 如何java面试_短时间如何过java面试?

    这题我会!作为一个编程界老司机,我曾总结过一套Java常见的面试考点大全,不知道帮助过多少程序员拿下offer. 现在我把这套Java面试大全放出来,希望对大家有所帮助! 本文内容过长,建议大家先赞后 ...

  7. 修改docker内java内存_在docker中使用java的内存情况

    Java和Docker不是天然的朋友. Docker可以设置内存和CPU限制,而Java不能自动检测到.使用Java的Xmx标识(繁琐/重复)或新的实验性JVM标识,我们可以解决这个问题. 虚拟化中的 ...

  8. java获取java版本_在运行时获取Java版本

    最简单的方法(java.specification.version): double version = Double.parseDouble(System.getProperty("jav ...

  9. 双表查询java代码_什么是JDBC?Java数据库连接性简介

    JDBC(Java数据库连接性)是Java API,用于管理与数据库的连接,发出查询和命令以及处理从数据库获得的结果集.JDBC在1997年作为JDK 1.1的一部分发布,是为Java持久层开发的首批 ...

最新文章

  1. linux 安装jdk tar.gz
  2. 中国LED产业园区现状模式及投资策略分析报告2022-2028年版
  3. sync是同步还是非同步_音视频是怎么保持同步的?(四)
  4. 第七章:在Spark集群上使用文件中的数据加载成为graph并进行操作(3)
  5. 深度探索Qt窗口系统——布局篇
  6. 大数据有哪些分析误区
  7. Pandas系列(十五)stack和pivot实现数据透视
  8. ios8 gps定位不好用
  9. 矩阵的对数运算公式_对数(运算层面)
  10. k3修改服务器,金蝶k3客户端修改服务器地址
  11. wifi 联想小新_联想小新如何开启wifi
  12. 计算机一级学科博士点的双非大学,2021年这些工学各学科,实力强劲的双非大学,强力推荐学生报考...
  13. html移动图片广告代码,右下角弹出广告代码 控制div移动 1)div是否
  14. 基于matlab深入形象理解频率分辨率,补零,栅栏效应,频谱泄漏
  15. 7.4V锂电池USB平衡充电器 串联锂电池充电器
  16. python爬虫入门之爬取英雄联盟官网的所有英雄数据
  17. 解决Mac苹果旧电脑、更换过硬盘时升级10.13以上系统提示“验证估计时发生错误”导致无法升级、u盘重装、改时间、官方版本也不行的问题
  18. Android画布放大缩小,android画板---涂鸦,缩放,旋转,贴纸实现
  19. Elasticsearch 入门案例
  20. [leetcode Q50] Pow(x, n)

热门文章

  1. 博文视点 OpenParty第11期:世界黑客大会那些事
  2. LeetCode—209. 长度最小的子数组
  3. pip 删除安装包_Python中PIP的快速指南
  4. 用户与硬件之间的接口
  5. 【整理】Spring 常用注解!千万不要错过!
  6. 设计模式:接口隔离原则
  7. JS字符串转换为JSON的四种方法笔记
  8. mysql强制禁止使用索引_MYSQL强制使用索引和禁止使用索引
  9. 全库模式 用户模式 表模式_暗模式,亮模式和用户的故事
  10. 34岁回顾人生,也怕中年危机!