Java 链表
按链表的组织形式分有ArrayList和LinkList两种。ArrayList内部其实是用数组的形式实现链表,比较适合链表大小确定或较少对链表进行增删操作的情况,同时对每个链表节点的访问时间都是constant;而LinkList内部以一个List实现链表,比较适合需要频繁对链表进行操作的情况,对链表节点的访问时间与链表长度有关O(N)。
另外,根据实现形式可以分为直接式(想不出什么合适的名字,姑且这样吧)和使用Iterator(迭代模式)两种方法。直接式的实现方法和C/C++中的写法差不多;而使用Iterator时,需要实现java.lan中的Iterable接口(或者也可以自己在链表内部定义自己的Iterator method)
使用迭代模式的优点:
1,实现功能分离,简化容器接口。让容器只实现本身的基本功能,把迭代功能委让给外部类实现,符合类的设计原则。
2,隐藏容器的实现细节。
3,为容器或其子容器提供了一个统一接口,一方面方便调用;另一方面使得调用者不必关注迭代器的实现细节。
4,可以为容器或其子容器实现不同的迭代方法或多个迭代方法。
我觉得第4点说的很好,对于一堆数据而言,不同的人(或业务逻辑)使用它的方式也不尽相同,定义一个theIterator继承Iterator,不仅提供next,hasNext 以及remove这个最小的操作集合,同时也可以提供更多的其它方法。在theIterator的实现类中,具体实现不同的迭代方法,比如顺序、逆序或根据其它语义进行遍历等,再通过类厂的方式将一个theIterator实现的对象交给用户使用。
下面我给出两个例子:
首先是直接式实现链表的例子,这个例子只提供了几种链表操作的基本方法,仅用于示意:
- public class MyList<AnyType> {
- private class Node<AnyType>{
- public Node<AnyType> pre;
- public Node<AnyType> next;
- public AnyType data;
- public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){}
- public Node(){}
- }
- private int theSize;
- private Node<AnyType> Header;
- private Node<AnyType> Tail;
- public MyList(){}
- public void add(AnyType item){}
- public boolean isEmpty(){}
- public int size(){}
- public AnyType get( int idx){}
- public void print(){}
- }
Node<AnyType>类定义了双向链表中节点的结构,它是一个私有类,而其属性和构造函数都是公有的,这样,其父类可以直接访问其属性,而外部类根本不知道Node类的存在。Data是节点中的数据与,pre指向前一个Node节点,next指向后一个Node节点。其构造函数的实现如下,不解释:
- public Node(AnyType d, Node<AnyType>p, Node<AnyType> n){
- this.data = d;
- this.pre = p;
- this.next = n;
- }
- public Node(){
- this.data = null;
- this.pre = null;
- this.next = null;
- }
下面我们看一下链表的构造函数实现:
- public MyList(){
- theSize = 0;
- Header = new Node<AnyType>(null,null,null);
- Tail = new Node<AnyType>(null,Header,null);
- Header.next = Tail;
- }
我们构造了一个带有头、尾节点的双向链表,头节点的Next指向尾节点,为节点的pre指向头节点。链表长度起始为0。
继续贴上链表类其它方法的实现,不解释了,应该比较清楚:
- public void add(AnyType item){
- Node<AnyType> aNode = new Node<AnyType>(item,null,null);
- Tail.pre.next = aNode;
- aNode.pre = Tail.pre;
- aNode.next = Tail;
- Tail.pre = aNode;
- theSize++;
- }
- public boolean isEmpty(){
- return ( theSize == 0);
- }
- public int size(){
- return theSize;
- }
- public AnyType get( int idx){
- if(idx > theSize-1 || idx < 0)
- throw new IndexOutOfBoundsException();
- Node<AnyType> current = new Node<AnyType>(null,Header,null);
- for(int i = 0; i<idx; i++)
- current = current.next;
- return current.data;
- }
- public void print(){
- Node<AnyType> current = Header.next;
- while(current.next != null){
- //如果AnyType是你自己定义 //的数据类型,那么请务必提供
- //一个toString方法,要么就不
- //要在链表里实现print方法。
- System.out.println(current.data.toString());
- current = current.next;
- }
- }
第二个例子是用迭代方法实现链表的例子,下面是类定义:
- public class MyListItr<Type> implements Iterable<Type> {
- private class Node<Type>{
- public Type data;
- public Node<Type> pre;
- public Node<Type> next;
- public Node(){}
- public Node(Type d, Node<Type> p, Node<Type> n){}
- }
- private Node<Type> Header;
- private Node<Type> Tail;
- private int theSize;
- public MyListItr(){}
- public void add(Type item){}
- public void print(){}
- public int size(){}
- @Override
- public Iterator<Type> iterator() {}
- private class MyListIterator implements Iterator<Type>{
- @Override
- public boolean hasNext() {}
- @Override
- public Type next() {}
- @Override
- public void remove() {}
- }
- }
这里主要说一下与前面例子不同的地方:MyListItr类实现了Java.lan.Itrable接口,因此我们必须实现其public Iterator<Type> iterator() {}方法,它的返回值是一个迭代器对象,那么我就定义一个内部私有类——MyListIterator,这个类实现了iterator接口。在public Iterator<Type> iterator() 方法中,我就构造这么一个MyListIterator的对象并返回。
Iterator接口有三个方法是我们必须实现的,hasNext,next和remove,这是迭代操作的最小集合了。对于不同的迭代器执行方式,我们可以定义多个类似MyListIterator这样的实现类,只要在链表类中的iterator() 方法中把具体实现类的对象返回给用户就OK了。
罗嗦两句:我感觉如果我们定义链表类时,如果不继承Iterable接口,而是直接在类内部提供 public theIterator Iterator() 方法,theIterator是一个自定义接口,继承自Iterator接口,这样我们可以提供遵循theIterator接口的各种迭代方式的不同实现类,并通过一个类厂方法在链表的public theIterator Iterator() 方法中把具体迭代实现类的对象交给用户,以此就可以根据不同的需求,提供链表的多种迭代方式。我觉得这种方法是可行的,但并没有具体实验。
下面只贴出链表iterator()方法和迭代实现类的MyListIterator代码,其它方法就不重复贴了:
- @Override
- public Iterator<Type> iterator() {
- return new MyListIterator();
- }
- private class MyListIterator implements Iterator<Type>{
- Node<Type> current = Header.next;
- @Override
- public boolean hasNext() {
- return (current != Tail);
- }
- @Override
- public Type next() {
- if(!hasNext())
- throw new IndexOutOfBoundsException();
- Type item = current.data;
- current = current.next;
- return item;
- }
- @Override
- public void remove() {
- if( !hasNext())
- throw new NoSuchElementException();
- current.pre.next = current.next;
- current.next.pre = current.pre;
- current = current.next;
- theSize--;
- }
- }
Java 链表的更多相关文章
- JAVA 链表操作:循环链表
主要分析示例: 一.循环链表简述 二.单链表循环链表 三.双链表循环链表 一.循环链表简述 循环链表即链表形成了一个循环的结构,尾节点不再指向NULL,而是指向头节点HEAD,此时判定链表的结束是尾节 ...
- Java链表基本操作和Java.util.ArrayList
Java链表基本操作和Java.util.ArrayList 今天做了一道<剑指offer>上的一道编程题“从尾到头打印链表”,具体要求如下:输入一个链表,按链表值从尾到头的顺序返回一个A ...
- JAVA链表中迭代器的实现
注:本文代码出自<java数据结构和算法>一书. PS:本文中类的名字定义存在问题,Link9应改为Link.LinkList9应该为LinkList.由于在同包下存在该名称,所以在后面接 ...
- JAVA 链表操作:单链表和双链表
主要讲述几点: 一.链表的简介 二.链表实现原理和必要性 三.单链表示例 四.双链表示例 一.链表的简介 链表是一种比较常用的数据结构,链表虽然保存比较复杂,但是在查询时候比较便捷,在多种计算机语言都 ...
- java 链表数据结构
首先,单链表相对于队列的优势在于存储地址不是连续的,这样的意义在于,操作其中的某一个位置的元素时不需要对之前的其他元素都进行内存操作,大大的为我们的计算机减压了.下面直接进入正题: 先要定义一个结点类 ...
- 算法_栈与队列的Java链表实现
链表是一个递归的数据结构,它或者为null,或者是指向一个结点的引用,该结点含有一个泛型的元素和指向另一个链表的引用.可以用一个内部类来定义节点的抽象数据类型: private class Node ...
- 学习记录 java 链表知识
01.import java.util.HashMap; 02.import java.util.Scanner; 03.import java.util.Stack; 04. 05./** 06. ...
- Java链表的一些操作:
[还有一些其他的算法提]http://blog.csdn.net/WalkingInTheWind/article/category/906980 [转]http://blog.csdn.net/lu ...
- 面试中的Java链表
链表作为常考的面试题,并且本身比较灵活,对指针的应用较多.本文对常见的链表面试题Java实现做了整理. 链表节点定义如下: static class Node { int num; Node next ...
随机推荐
- swift相关
1.界面上一个输入框,一个按钮,一个webview .点按钮,webview显示输入框中输入的url的页面. @IBOutlet var wv:UIWebView = nil //定 ...
- 【JAVA并发编程实战】10、并发程序的测试
1.产生随机数 package cn.study.concurrency.ch12; public class Util { public static int xorShift(int y) { / ...
- hadoop 集群的配置
在经过几天折腾,终于将hadoop环境搭建成功,整个过程中遇到各种坑,反复了很多遍,光虚拟机就重新安装了4.5次,接下来就把搭建的过程详细叙述一下 0.相关工具: 1,系统环境说明: 我这边给出我的集 ...
- JAVAScript柯里化、部分应用参数终极理解
一.柯里化 在定义柯里化.部分应用参数的概念前,首先必须对闭包有深入的了解和定义,闭包一句话说清楚:函数返回值为函数. 柯里化的定义:将多参函数分解为按步骤接受单个参数的函数,如下代码: var mo ...
- 纯CSS3实现3D跳动小球
请使用Chrome,火狐的浏览器查看本页面,使用IE将看不到效果.如果在本页看不到一个跳动的小球,请确定您的浏览器支持CSS3,或者访问http://keleyi.com/a/bjac/iphgrtq ...
- JavaScript进阶篇QA总结
Q1:常用的运算符有哪些?他们的优先级是怎样的?A1:1.算术运算符:加(+).减(-).乘(×).除(÷),自加一(++),自减一(--):2.比较运算符:大于(>).小于(<).大于等 ...
- “.”(十六进制值 0x00)是无效的字符解决方案
自从我们的项目数据层从读取数据库改为读取接口服务后,经常会出现一些类似于的错误.我们的数据结构如下所示 <type><![CDATA[gp]]></type> &l ...
- HTML DOM 教程
HTML DOM DOM 教程 DOM 简介 DOM 节点 DOM 方法 DOM 属性 DOM 访问 DOM 修改 DOM 内容 DOM 元素 DOM 事件 DOM 导航 一,HTML DOM 简介 ...
- 让ABAP开发者更加轻松的若干快捷键
引言 ABAP是一种和当代编程语言在许多方面有着相当不同的编程语言.ABAP的某些方面可能会让我们奇怪,为什么它会如此复杂?而它的某些方面又是那么杰出,给予了ABAP开发者们比其它任何语言更多的便利. ...
- iOS 十六进制的颜色值转换为UIColor
UIColor+Hex.h里面中 #import <UIKit/UIKit.h> @interface UIColor (Hex) + (UIColor *) colorWithHexSt ...