[Java]ArrayList与LinkedList的模拟实现

    Java中的List继承自Collection接口。List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。跟Set集合不同的是,List允许有重复元素。对于满足e1.equals(e2)条件的e1与e2对象元素,可以同时存在于List集合中。当然,也有List的实现类不允许重复元素的存在。除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。

    实现List接口的常用类有LinkedList,ArrayList,这里模拟两种List的特点进行实现从而加深理解。


一、 双向链表LinkedSequence:


a) 私有成员变量:

	private Entry head;
	private int cnt;

i. head为Entry型的每个LinkedSequence头指针,通过它能够访问该对象的所有内部数据,注意head不存储数据,只是用作头节点标记。
Entry结构具体在下面介绍。
ii. cnt记录每个LinkedSequence内部的成员个数,为了方便内部类的使用,创建了私有方法setSize()对齐进行修改。


b) 私有内部静态类:

private static class Entry {
	SequenceItem si;
	Entry befo;
	Entry next;
	Entry(SequenceItem si, Entry befo, Entry next) {
		this.si = si;
		this.befo = befo;
		this.next = next;
	}
}

每个实体含有一个SequenceItem型的私有成员变量si,指向前一个Entry的引用befo,指向下一个Entry的引用next。
c) 构造函数:
LinkedSequence() {
	head = new Entry(null, null, null);
	head.next = head;
	head.befo = head;
	cnt = 0;
}

i. 将head初始化,所有数据成员均为空;
ii. 将head的next和befo均标记为自己;
iii. cnt计数清零。


d) 公有方法:

i. public void add(SequenceItem item)链表尾段添加item元素:
创建一个Entry型的tmp,它的si为item,befo即head的befo(head的befo指向链表中的最后一个Entry),next即head。
将原来的末端元素的next标记为tmp,将head的befo指向新的末端元素tmp。
计数器cnt加一。

ii. public SequenceItem get(int i)获取链表中第i个元素,起始下标为0:
如果索引i超出[0, cnt),则抛出越界异常。
创建Entry e指向第一个元素,通过next向后迭代i次,返回此时的e.si。

iii. public void remove(SequenceItem item)删除链表中的第一个item元素:
如果item为空,不做处理;
创建Entry e指向第一个元素,根据e扫描链表,如果有一个e的si与item相等,创建一个Entry tmp指向此时的e,将e的前驱节点的next标记为e的后继节点,将e的后继节点的befo标记为e的前驱结点。
将tmp的befo、next和si均标记为空,计数器cnt减一。
如果遍历整个列表结束后没有找到,则抛出NoSuchElementException异常。

iv. public boolean contains(SequenceItem item)判断链表中是否含有item元素:
与remove()操作类似,只不过返回true和false不做敖述。

v. public int size()返回链表的长度:返回cnt。

vi. public boolean isEmpty()判断链表是否为空:
返回cnt==0?

vii. public SequenceItem[] toArray()将链表转化为数组:
创建一个大小为cnt的SequenceItem数组ans,依次将链表中每个节点e的si放入ans即可。

viii. public boolean equals(Sequence seq)判断两个链表元素是否相对:
如果两个Sequence的大小不等,则不再进行比较,直接返回false。
否则依次比较两个sequence的全部元素,一旦含有一个不同则返回false。

ix. public String toString()将链表转化为字符串:
与toArray类似,只不过将原来的ans用string存储,并在放入结束后返回Arrays.toString(ans)。

x. public SeqIterator iterator()正向迭代器:
返回一个SeqIterator的内部匿名类对象,含有私有成员变量index,记录当前访问元素的相对位置,初始化为0;Entry e记录当前将要访问的节点,初始化为链表的第一个元素。
public boolean hasNext():返回index是否小于LinkedSequence.this.size()。
public SequenceItem next():返回当前e.si,index加1,e指向e.next。
public void remove():只有当index大于0时,才可以将前一个元素删除,删除操作同remove()。注意删除后需要将index减一!

xi. public SeqIterator reverseIterator()反向迭代器:
返回一个SeqIterator的内部匿名类对象,含有私有成员变量index,记录当前访问元素的相对位置,初始化为size()-1;Entry e记录当前将要访问的节点,初始化为链表的最后一个元素。
public boolean hasNext():返回index是否大于-1。
public SequenceItem next():返回当前e.si,index减1,e指向e.befo。
public void remove():只有当index小于size()-1时,才可以将后一个元素删除,删除操作同remove()。注意此时无需修改index。

xii. public BiSeqIterator biIterator()双向迭代器:
即前两种迭代器的综合。


二、 变长数组ArraySequence:

a) 私有成员变量:

	private SequenceItem[] sequenceItemArray = new SequenceItem[2];
	private int cnt = 0;

i. sequenceItemArray为每个ArraySequence数据存储数组。
ii. cnt记录每个ArraySequence内部的成员个数,与数组大小不同。

b) 构造函数:

ArraySequence() {
	sequenceItemArray = new SequenceItem[2];
	cnt = 0;
}

i. 将sequenceItemArray初始化大小为2;
ii. cnt计数清零。

c) 公有方法:基本与LinkedSequence类似,主要区别在于add()和remove()操作。
i. public void add(SequenceItem item)数组中添加item元素:
首先确保sequenceItemArray的大小可以装下cnt+1个元素,如果超出sequenceItemArray的大小,需要将当前数组大小扩充为原来的一倍,再将item放入,计数器cnt加一。

ii. public void remove(SequenceItem item)删除链表中的第一个item元素:
首先在sequenceItemArray中寻找item的下标,记为i,则后面的cnt-i-1个元素都需要前移,之后将cnt-1下标处置为空。
以上完成了删除操作,需要对容器的大小重新确定,如果当前cnt不足sequenceItemArray长度的1/4,则可将sequenceItemArray的长度缩短一半,方法类似于扩张的方法。

三、 性能比较:

四、源码:

Sequence接口:

import java.util.*;
interface Sequence {
	void add(SequenceItem item);
	SequenceItem get(int i);
	void remove(SequenceItem item);
	boolean contains(SequenceItem item);
	int size();
	boolean isEmpty();
	SeqIterator iterator();
	SeqIterator reverseIterator();
	BiSeqIterator biIterator();
	SequenceItem[] toArray();
	boolean equals(Sequence seq);
	String toString();
}

SeqIterator接口:

interface SeqIterator {
	boolean hasNext();
	SequenceItem next();
	void remove();
}

数据成员SequenceItem类:

public class SequenceItem {
	private String data;
	public String getData() {
		return data;
	}
	public void setData(String s) {
		data = new String(s);
	}
	public boolean equals(SequenceItem si) {
		return this.getData().equals(si.getData());
	}
}

双向链表LinkedSequence:

import java.util.*;
public class LinkedSequence implements Sequence {
	private Entry head;
	private int cnt;
	private static class Entry {
		SequenceItem si;
		Entry befo;
		Entry next;
		Entry(SequenceItem si, Entry befo, Entry next) {
			this.si = si;
			this.befo = befo;
			this.next = next;
		}
	}
	LinkedSequence() {
		head = new Entry(null, null, null);
		head.next = head;
		head.befo = head;
		cnt = 0;
	}
	public void add(SequenceItem item) {
		Entry tmp = new Entry(item, head.befo, head);
		tmp.befo.next = tmp;
		head.befo = tmp;
		cnt++;
	}
	public SequenceItem get(int i) {
		if (i >= cnt || i < 0) {
			throw new IndexOutOfBoundsException();
		}
		Entry e = head.next;
		for (int j = 0; j < i; j++) {
			e = e.next;
		}
		return e.si;
	}
	public void remove(SequenceItem item) {
		if (item == null) {
			return;
		}
		Entry e = head.next;
		for (int i = 0; i < cnt; i++) {
			if (item.equals(e.si)) {
				Entry tmp = e;
				tmp.befo.next = tmp.next;
				tmp.next.befo = tmp.befo;
				tmp.befo = null;
				tmp.next = null;
				tmp.si = null;
				cnt--;
				return;
			}
			e = e.next;
		}
		throw new NoSuchElementException();
	}
	public boolean contains(SequenceItem item) {
		if (item == null) {
			return false;
		}
		Entry e = head.next;
		for (int i = 0; i < cnt; i++) {
			if (item.equals(e.si)) {
				return true;
			}
			e = e.next;
		}
		return false;
	}
	public int size() {
		return this.cnt;
	}
	private void setSize(int cnt) { // use for inner class to modify cnt
		this.cnt = cnt;
		// System.out.println("cnt:"+this.cnt);
	}
	public boolean isEmpty() {
		return this.cnt==0 ? true:false;
	}
	public SeqIterator iterator() {
		return new SeqIterator() {
			private int index = 0;
			private Entry e = head.next;
			public boolean hasNext() {
				return index<LinkedSequence.this.size();
			}
			public SequenceItem next() {
				SequenceItem res = e.si;
				e = e.next;
				index++;
				return res;
			}
			public void remove() {
				if (index > 0) {
					Entry tmp = e.befo;
					tmp.befo.next = tmp.next;
					tmp.next.befo = tmp.befo;
					tmp.befo = null;
					tmp.next = null;
					tmp.si = null;
					index--;
					LinkedSequence.this.setSize(LinkedSequence.this.size()-1);
				}
			}
		};
	}
	public SeqIterator reverseIterator() {
		return new SeqIterator() {
			private int index = LinkedSequence.this.size()-1;
			private Entry e = head.befo;
			public boolean hasNext() {
				return index>-1;
			}
			public SequenceItem next() {
				SequenceItem res = e.si;
				e = e.befo;
				index--;
				return res;
			}
			public void remove() {
				if (index < LinkedSequence.this.size()-1) {
					Entry tmp = e.next;
					tmp.befo.next = tmp.next;
					tmp.next.befo = tmp.befo;
					tmp.befo = null;
					tmp.next = null;
					tmp.si = null;
					LinkedSequence.this.setSize(LinkedSequence.this.size()-1);
				}
			}
		};
	}
	public BiSeqIterator biIterator() {
		return new BiSeqIterator() {
			private int index = 0;
			private Entry e = head.next;
			public boolean hasNext() {
				return index<LinkedSequence.this.size();
			}
			public SequenceItem next() {
				SequenceItem res = e.si;
				e = e.next;
				index++;
				return res;
			}
			public void remove() {
				if (index > 0) {
					Entry tmp = e.befo;
					tmp.befo.next = tmp.next;
					tmp.next.befo = tmp.befo;
					tmp.befo = null;
					tmp.next = null;
					tmp.si = null;
					index--;
					LinkedSequence.this.setSize(LinkedSequence.this.size()-1);
				}
			}
			public boolean hasPrevious() {
				// System.out.println(index);
				return index>-1;
			}
			public SequenceItem previous() {
				SequenceItem res = e.si;
				e = e.befo;
				index--;
				return res;
			}
		};
	}
	public SequenceItem[] toArray() {
		SequenceItem[] ans = new SequenceItem[cnt];
		Entry e = head.next;
		for (int i = 0; i < cnt; i++) {
			ans[i] = e.si;
			e = e.next;
		}
		return ans;
	}
	public boolean equals(Sequence seq) { //?
		if (this.size() != seq.size()) {
			return false;
		}
		Entry e1 = this.head.next;
		for (int i = 0; i < cnt; i++) {
			if (!e1.si.equals(seq.get(i))) {
				return false;
			}
			e1 = e1.next;
		}
		return true;
	}
	public String toString() {
		String[] s = new String[cnt];
		Entry e = head.next;
		for (int i = 0; i < cnt; i++) {
			s[i] = e.si.getData();
			e = e.next;
		}
		return Arrays.toString(s);
	}
}

变长数组ArraySequence:

import java.util.*;
public class ArraySequence implements Sequence {
	private SequenceItem[] sequenceItemArray;
	private int cnt;
	ArraySequence() {
		sequenceItemArray = new SequenceItem[2];
		cnt = 0;
	}
	private void ensureCapacity(int currCapacity) {
		if (currCapacity >= sequenceItemArray.length) {
			int newCapacity = sequenceItemArray.length<<1;
			sequenceItemArray = Arrays.copyOf(sequenceItemArray, newCapacity);
		}
		else if (currCapacity < sequenceItemArray.length/4) {
			int newCapacity = sequenceItemArray.length>>1;
			sequenceItemArray = Arrays.copyOf(sequenceItemArray, newCapacity);
		}
	}
	public void add(SequenceItem item) {
		ensureCapacity(cnt+1);
		sequenceItemArray[cnt++] = item;
	}
	public SequenceItem get(int i) {
		return sequenceItemArray[i];
	}
	public void remove(SequenceItem item) {
		if (item != null) {
			for (int i = 0; i < cnt; i++) {
				if (item.equals(sequenceItemArray[i])) {
					int numMoved = cnt-i-1;
					if (numMoved > 0) {
						System.arraycopy(sequenceItemArray, i+1, sequenceItemArray, i, numMoved);
					}
					sequenceItemArray[--cnt] = null;
					ensureCapacity(cnt);
					return;
				}
			}
		}
	}
	public boolean contains(SequenceItem item) {
		for (int i = 0; i < cnt; i++) {
			if (item.equals(sequenceItemArray[i])) {
				return true;
			}
		}
		return false;
	}
	public int size() {
		// System.out.println(cnt+","+sequenceItemArray.length);
		return cnt;
	}
	public boolean isEmpty() {
		return cnt==0 ? true:false;
	}
	public SeqIterator iterator() {
		return new SeqIterator() {
			private int index = 0;
			public boolean hasNext() {
				return index<cnt;
			}
			public SequenceItem next() {
				return sequenceItemArray[index++];
			}
			public void remove() {
				if (index > 0) {
					ArraySequence.this.remove(ArraySequence.this.get(--index));
				}
			}
		};
	}
	public SeqIterator reverseIterator() {
		return new SeqIterator() {
			private int index = ArraySequence.this.size()-1;
			public boolean hasNext() {
				return index>-1;
			}
			public SequenceItem next() {
				return ArraySequence.this.get(index--);
			}
			public void remove() {
				ArraySequence.this.remove(ArraySequence.this.get(index+1));
				if (index >= ArraySequence.this.size()) {
					index = ArraySequence.this.size()-1;
				}
			}
		};
	}
	public BiSeqIterator biIterator() {
		return new BiSeqIterator() {
			private int index = 0;
			public boolean hasNext() {
				return index<ArraySequence.this.size();
			}
			public SequenceItem next() {
				return ArraySequence.this.get(index++);
			}
			public void remove() {
				if (index > 0) {
					ArraySequence.this.remove(ArraySequence.this.get(--index));	
				}
			}
			public boolean hasPrevious() {
				return index>0;
			}
			public SequenceItem previous() {
				return ArraySequence.this.get(--index);
			}
		};
	}
	public SequenceItem[] toArray() {
		return sequenceItemArray;
	}
	public boolean equals(Sequence seq) {
		if (this.size() !=seq.size()) {
			return false;
		}
		for (int i = 0; i < this.size(); i++) {
			if (!this.get(i).equals(seq.get(i))) {
				return false;
			}
		}
		return true;
	}
	public String toString() {
		String[] s = new String[cnt];
		for (int i = 0; i < cnt; i++) {
			s[i] = sequenceItemArray[i].getData();
		}
		return Arrays.toString(s);
	}
}

附测试程序test.java:

import java.util.*;
public class test {
	public static void main(String[] args) {
		// Sequence test = new LinkedSequence();
		Sequence test = new ArraySequence();
		SequenceItem tmp = new SequenceItem();
		// Test add(), size()
		for (int cnt = 1000; cnt <= 1000000; cnt *= 10) {
			int testTime = 10;
			double ans = 0;
			double[] res = new double[testTime];
			while (testTime > 0) {
				// Date begin = new Date();
				for (int i = 0; i < cnt; i++) {
					tmp.setData(String.valueOf(i));
					test.add(tmp);
					// System.out.println(test.size());
				}
				// Date end = new Date();

				// Date begin = new Date();
				// for (int i = 0; i < cnt; i++) {
				// 	tmp = test.get(0);
				// 	// System.out.println(tmp.getData());
				// 	test.remove(tmp);
				// }
				// Date end = new Date();

				// Date begin = new Date();
				// for (int i = 0; i < cnt; i++) {
				// 	tmp = test.get(i);
				// 	// System.out.println(tmp.getData());
				// }
				// Date end = new Date();			

				SeqIterator si = test.iterator();
				Date begin = new Date();
				while (si.hasNext()) {
					si.next();
				}
				Date end = new Date();
				res[--testTime] = ((double)end.getTime()-begin.getTime())/1000;
				ans += res[testTime];
			}
			System.out.println(Arrays.toString(res));
			System.out.println(ans);
		}

		// Test add(), get()
		// for (int i = 0; i < 10; i++) {
		// 	SequenceItem tmp = new SequenceItem();
		// 	tmp.setData(String.valueOf(i));
		// 	test.add(tmp);
		// 	System.out.println(test.get(i).getData());
		// }

		// Test iterator(), reverseIterator()
		// SeqIterator si = test.iterator();
		// while (si.hasNext()) {
		// 	System.out.println(si.next().getData());
		// 	si.remove();
		// }
		// System.out.println(test.isEmpty());
		// si = test.reverseIterator();
		// while (si.hasNext()) {
		// 	System.out.println(si.next().getData());
		// 	si.remove();
		// }
		// System.out.println(test.isEmpty());

		// Test biIterator()
		// BiSeqIterator bsi = test.biIterator();
		// while (bsi.hasNext()) {
		// 	System.out.println("test next:");
		// 	System.out.println(bsi.next().getData());
		// 	// bsi.remove();
		// }
		// System.out.println(test.isEmpty());
		// bsi.previous();
		// while (bsi.hasPrevious()) {
		// 	System.out.println("test previous:");
		// 	System.out.println(bsi.previous().getData());
		// }
		// System.out.println(test.isEmpty());

		// Test toString()
		// System.out.println(test);

		// Test isEmpty(), get(), remove(), contains(), size()
		// while (!test.isEmpty()) {
		// 	SequenceItem tmp = test.get(0);
		// 	System.out.println(tmp.getData());
		// 	test.remove(tmp);
		// 	System.out.println(test.size());
		// 	System.out.println(test.contains(tmp));
		// }
	}
}




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用 LinkedListArrayList 集合实现的 KTV 点歌系统的 Java 代码: ```java import java.util.LinkedList; import java.util.ArrayList; import java.util.Scanner; public class KTVSystem { public static void main(String[] args) { LinkedList<String> songList1 = new LinkedList<>(); // 使用 LinkedList 集合实现 ArrayList<String> songList2 = new ArrayList<>(); // 使用 ArrayList 集合实现 Scanner sc = new Scanner(System.in); songList1.add("稻香"); songList1.add("夜曲"); songList1.add("夜的第七章"); songList1.add("听妈妈的话"); songList1.add("龙卷风"); songList2.add("稻香"); songList2.add("夜曲"); songList2.add("夜的第七章"); songList2.add("听妈妈的话"); songList2.add("龙卷风"); while (true) { System.out.println("请选择操作:0-添加歌曲;1-将歌曲置顶;2-将歌曲前移一位;3-退出"); int option = sc.nextInt(); if (option == 0) { System.out.println("请输入要添加的歌曲名称:"); String song = sc.next(); songList1.add(song); songList2.add(song); System.out.println("已添加歌曲:" + song); System.out.println("当前歌曲列表(LinkedList):" + songList1); System.out.println("当前歌曲列表(ArrayList):" + songList2); } else if (option == 1) { System.out.println("请输入要置顶的歌曲名称:"); String song = sc.next(); if (songList1.remove(song)) { songList1.addFirst(song); System.out.println("已将歌曲" + song + "置顶(LinkedList)"); } else { System.out.println("歌曲" + song + "不存在!"); } if (songList2.remove(song)) { songList2.add(0, song); System.out.println("已将歌曲" + song + "置顶(ArrayList)"); } else { System.out.println("歌曲" + song + "不存在!"); } System.out.println("当前歌曲列表(LinkedList):" + songList1); System.out.println("当前歌曲列表(ArrayList):" + songList2); } else if (option == 2) { System.out.println("请输入要置前的歌曲名称:"); String song = sc.next(); int index1 = songList1.indexOf(song); int index2 = songList2.indexOf(song); if (index1 != -1) { songList1.remove(index1); songList1.add(index1 - 1, song); System.out.println("已将歌曲" + song + "置前一位(LinkedList)"); } else { System.out.println("歌曲" + song + "不存在!"); } if (index2 != -1) { songList2.remove(index2); songList2.add(index2 - 1, song); System.out.println("已将歌曲" + song + "置前一位(ArrayList)"); } else { System.out.println("歌曲" + song + "不存在!"); } System.out.println("当前歌曲列表(LinkedList):" + songList1); System.out.println("当前歌曲列表(ArrayList):" + songList2); } else if (option == 3) { System.out.println("已退出系统。"); break; } else { System.out.println("无效指令!"); } } } } ``` 注意,上述代码中的 LinkedListArrayList 集合都存储了相同的歌曲列表,因此添加、置顶和置前操作都同时在两个集合上进行,并在每次操作后都输出当前的歌曲列表,方便查看。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值