一道题引出对LinkedList源码的研究
题目:打开一个文本文件,每次读取一行内容,将每行作为一个String读入,并将那个String对象置入一个LinkedList中,按照相反的顺序打印出LinkedList中的所有行.
解题代码:
public static List<String> read(String filename) throws Exception {
String filepath = "path";
FileReader fileReader = new FileReader(filepath + filename);
BufferedReader in = new BufferedReader(fileReader);
List<String> list = new LinkedList<String>();
String s;
while((s = in.readLine()) != null)
list.add(s + "\n");
in.close();
return list;
}
public static void main(String[] args) throws Exception{
List<String> list = read("filename");
for(ListIterator<String> it = list.listIterator(list.size()); it.hasPrevious();)
System.out.print(it.previous());
}
for(ListIterator<String> it = list.listIterator(list.size()); it.hasPrevious();)
System.out.print(it.previous());
public interface ListIterator<E> extends Iterator<E> {
//Iteator接口定义的方法.
boolean hasNext();
E next();
void remove(); boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void set(E e);
void add(E e);
}
在看一下LinkedList类中对ListIterator接口的实现:
private class ListItr implements ListIterator<E> {
//lastReturned为最近返回的节点,当调用previous()时,lastReturned = next;
private Node<E> lastReturned;
//next为当前节点的下一个节点.
private Node<E> next;
//nextIndex的范围是0-(size-1)(链表的长度减一).表示下个节点在链表中的位置.
private int nextIndex;
private int expectedModCount =modCount; ListItr(int index) {
/*
*如果index等于size,则表示要跳转到链表的末端,则next=null,否则调用node(index).
*nextIndex被赋值为index.
*/
next = (index == size) ? null : node(index);
nextIndex = index;
}
//nextIndex的合理范围在0-(size-1)
public boolean hasNext() {
return nextIndex < size;
} public E next() {
checkForComodification();
//判断是否有下一个值.
if(!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
//nextIndex的范围在0-(size-1),所以只要nextIndex>0,就表示当前节点有前节点.
public boolean hasPrevious() {
return nextIndex > 0;
}
//当调用previous时,lastReturned等于next.
public E previous() {
checkForComodification();
if(!hasPrevious())
throw new NoSuchElementException();
将lastReturned = next.
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
} public int nextIndex() {
return nextIndex;
} public int previousIndex() {
return nextIndex - 1;
}
//remove()移除的是lastReturned节点.
public void remove() {
checkForComodification();
if(lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
//移除lastReturned
/*
*首先要考虑如果要删除的是链表中的第一个/最后一个节点.
*按照正常情况:
*lastReturned.prev.next = lastReturned.next;
*lastReturned.next.prev = lastReturned.prev;
*lastReturned.next,lastReturned.prev,lastReturned.item设置为null;
*链表长度减一.
*/
unlink(lastReturned);
if(next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
} public void set(E e) {
if(lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
//向链表中添加节点.
public void add(E e) {
checkForComodification();
lastReturned = null;
//如果next==null,表示要插入到链表的末端.
if(next == null)
linkLast(e);
//否则插入到链表
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
} final void checkForComodification() {
if(modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
//采用二分法的形式,跳转的链表中第index个节点.
Node<E> node(int index) {
// assert isElementIndex(index);
//size >> 1 表示size/2
if (index < (size >> 1)) {
//first是链表的第一个节点
Node<E> x = first;
//从前向后查找
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//last是链表的最后一个节点.
//从后向前查找.
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
//
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//如果要删除的是第一个元素,则x.prev == null;需要将链表中的第一个节点first标记为next.
if(prev == null)
first = next;
//否则按照正常逻辑x.prev.next = x.next.在将当前节点的prev设置为null.
else {
prev.next = next;
x.prev = null;
}
//如果要删除的是最后一个元素,则x.next== null;需要将链表中最后一个几点last标记为prev.
if(next == null)
last = prev;
//否则按照正常逻辑x.next.prev = x.prev.将当前节点的next设置为null.
else {
next.prev = prev;
x.next = null;
}
//将当前节点的item设置为null.
x.item = null;
//链表长度减一.
size--;
//修改次数加一.
modCount++;
return element;
}
/*
*向链表的末端添加节点.这里需要考虑如果链表为空,链表不为空.
*要写出优秀的代码,要将链表为空和不为空都执行的部分提取出来,之后在考虑不一样的情况.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<E>(l, e, null);
last = node;
//l == null 的话,表示链表为空.将first设置为newNode
if(l == null)
first = newNode;
//如果链表不为空,将原来链表的最后一个节点.next = newNode.
else
l.next = newNode;
size++;
modCount++;
}
/*在succ节点前,添加节点.要考虑succ是不是头节点.
*
*/
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
Node<E> newNode = new Node<E>(pred, e, succ);
succ.prev = newNode;
//如果succ是头节点,则pred = succ.prev == null.将头节点设置为newNode
if(pred == null)
first = newNode;
else
prev.next = newNode;
size++;
modCount++;
}
一道题引出对LinkedList源码的研究的更多相关文章
- 给jdk写注释系列之jdk1.6容器(2)-LinkedList源码解析
LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明. 1.链表的概念 链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链 ...
- LinkedList源码解析
LinkedList是基于链表结构的一种List,在分析LinkedList源码前有必要对链表结构进行说明.1.链表的概念链表是由一系列非连续的节点组成的存储结构,简单分下类的话,链表又分为单向链表和 ...
- LinkedList源码解读
一.内部类Node数据结构 在讲解LinkedList源码之前,首先我们需要了解一个内部类.内部类Node来表示集合中的节点,元素的值赋值给item属性,节点的next属性指向下一个节点,节点的pre ...
- ArrayList和LinkedList源码
1 ArrayList 1.1 父类 java.lang.Object 继承者 java.util.AbstractCollection<E> 继承者 java.util.Abstract ...
- 转:【Java集合源码剖析】LinkedList源码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/35787253 您好,我正在参加CSDN博文大赛,如果您喜欢我的文章,希望您能帮我投一票 ...
- java基础解析系列(十)---ArrayList和LinkedList源码及使用分析
java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...
- LinkedList源码和并发问题分析
1.LinkedList源码分析 LinkedList的是基于链表实现的java集合类,通过index插入到指定位置的时候使用LinkedList效率要比ArrayList高,以下源码分析是基于JDK ...
- ArrayList 和 LinkedList 源码分析
List 表示的就是线性表,是具有相同特性的数据元素的有限序列.它主要有两种存储结构,顺序存储和链式存储,分别对应着 ArrayList 和 LinkedList 的实现,接下来以 jdk7 代码为例 ...
- Android版数据结构与算法(三):基于链表的实现LinkedList源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. LinkedList 是一个双向链表.它可以被当作堆栈.队列或双端队列进行操作.LinkedList相对于ArrayList来说,添加,删除元素效 ...
随机推荐
- 使用Druid作为SpringBoot项目数据源(添加监控)
Druid是一个关系型数据库连接池,它是阿里巴巴的一个开源项目.Druid支持所有JDBC兼容数据库,包括了Oracle.MySQL.PostgreSQL.SQL Server.H2等.Druid在监 ...
- PlayFramework 一步一步来 之 页面模板引擎
Play的魔板引擎本人认为可以说是为full stack Developers量身打造的功能.在原有的html页面基础上,只需要在html文件名后缀名前面加上”.scala“,就可以在页面上写Scal ...
- Android 系统启动过程简单记录
本文记录Android系统启动过程,包含从linux kernerl到luancher启动完成的过程: 1.linux内核完成系统设置后,会在系统文件中寻找‘init’文件,然后启动root进程或者说 ...
- sourceInsight4 完美破解
sourceInsight4 完美破解 参考路径: https://blog.csdn.net/zxy020/article/details/75047670 首先确保你在官网下载了原版4.0并安装好 ...
- 16.The Effect of Advertisement 广告的影响
16.The Effect of Advertisement 广告的影响 (1) The appeal of advertising to buying motives can have both n ...
- 取消Debian屏保及显示器休眠
在产品展示场合,屏保及休眠会带来不好的体验,很多时候需要关闭掉. dpms显示器休眠设置: 开启:$ sudo xset dpms 1 1 2取消:$ sudo xset -dpms xset设置屏保 ...
- self_vs_default_definee_vs_receiver
最近在学习ruby的过程遇到很多有趣的博客,随记录学习,这篇学习笔记摘自http://yugui.jp/articles/846 #self ruby中self无处不在,或是显示的调用或是隐含调用,方 ...
- 利用openxml在Excel中插入图表
using System.Collections.Generic; using System.Linq; using DOD = DocumentFormat.OpenXml.Drawing; usi ...
- java面试问题收集(2)
1 Integer int相等问题 Integer对象和int比较的时候会有一个拆箱的过程,始终相等 Integer和new Integer对象不会相等,引用不同 两个Integer对象比较,Inte ...
- C# 自动程序 windows 无法启动 XXXX 服务 错误5 拒绝访问
遇到过两次 这样的问题了,所以记录一下 原因可能是服务所在文件的目录权限不够 解决方法: 1是查看服务对应的程序所在的目录 2是设置目录的安全权限 右击–属性–安全–添加相应的帐号,给予除完全控制外的 ...