Java实现单向链表(基于Collection扩展

1 新增需求

       前面我们已经实现了一个简单的单向链表,虽然从功能上来说,它可以满足一个单向链表的基本需求,但如果放在整个Java体系中,这样的链表是无法融入的。在Java体系中,链表属于Collection集合体系下的集合容器(并且Collection的实现类已有实现),我们必须要让自定义链表实现Collection接口。

2 新增功能

        新增的功能主要是对Collection接口中定义的方法进行实现,在之前的单向链表中,我们已经实现了部分方法,这样可以减少一定的工作量。Collection的方法如表1所示。

类型方法名称方法描述
booleanadd(E e) √向集合添加元素,返回添加结果。
booleanaddAll(Collection<? extends E> c)将指定集合中的所有元素添加到此集合中。
voidclear()从该集合中删除所有元素。
booleancontains(Object o)√判断此集合中是否包含指定的元素。
booleancontainsAll(Collection<?> c)判断此集合包含指定集合中的所有元素。
booleanequals(Object o)将指定对象与此集合进行比较以获得相等性。
inthashCode()返回此集合的哈希码值。
booleanisEmpty()如果此集合不包含任何元素,则返回true。
Iteratoriterator()返回此集合中元素的迭代器。
booleanremove(Object o)从该集合中删除指定元素的单个实例(如果存在)。
booleanremoveAll(Collection<?> c)删除指定集合中包含的所有此集合的元素。
booleanretainAll(Collection<?> c)仅保留指定集合中包含的此集合中的元素。
intsize()√返回此集合中元素的数量。
Object[]toArray()返回包含此集合中所有元素的数组。
T[]toArray(T[] a)返回包含此集合中所有元素的数组; 返回数组的运行时类型是指定数组的运行时类型。

       对比我们已经实现的方法和Collection接口中的方法,其中三个方法是已经实现了(√号标识的方法),剩余方法是还需要我们进行实现的。

       Collection接口中的15个方法,3个方法我们已经实现,hashCode和equals方法我们可以不用实现(借用Object类的该方法),其余的10个方法我们总结如下:

  • boolean addAll(Collection<? extends E> c)
  • booleancontainsAll(Collection<?> c)
  • boolean removeAll(Collection<?> c)
  • boolean retainAll(Collection<?> c)

       这四个方法的参数都是一个Collection集合,在操作参数对象时,我们势必要将集合中的元素逐一遍历出来,Collection集合能够遍历的基础在于迭代器Iterator的使用。除了containsAll方法外,其它三个方法还需要对链表的长度成员变量(size)进行更新。

  • Iterator iterator():iterator方法要返回一个Iterator对象,我们可以在该方法中用内部类的方式实现一个Iterator接口,实现该接口需要重写方法hasNext()和next(),hasNext方法返回一个布尔值,用于表示是否有下一个元素需要遍历。next方法返回下一个元素对象。

       iterator()是Collection接口中非常重要的方法,即迭代器接口。我们在迭代集合的时候,通常是通过它来迭代的。在Collection的其它方法中,只要涉及到两个集合的相互操作(合并、删除、查找)都会依赖Iterator迭代器来完成。

  • void clear():清空链表,同时注意头、尾节点以及size的重置。
  • boolean remove(Object o):该方法和基础链表中的remove方法形成重载,但该方法可以将链表中的所有指定实例o删除。
  • boolean equals(Object o):建议直接使用Object的equals方法。如果不熟悉equals和hashCode的重写规则,建议不重写该方法。
  • int hashCode():建议直接使用Object的hashCode方法。
  • boolean isEmpty():如果size为0,返回true。
  • Object[] toArray():将链表转化成数组,根据size大小创建同等大小的Object数组,通过遍历的方式,将链表中的元素放入数组中。最后返回数组。
  • < T > T[] toArray(T[] a):将链表元素放到泛型数组a中,注意这里面的泛型标识符T与定义的泛型标识符不是同一个类型,在转化的过程中需要转型。提供的泛型数组长度可能与链表的长度一致,如果a的长度小,则截取一部分链表元素放入a中,如果a的长度过长,则a多余部分用null代替。

2.1 迭代功能实现分析

       迭代器Iterator的实现主要实现hasNext和next方法,迭代器的最低实现要求是能够将链表中的所有节点数据遍历出来。

       hasNext方法可以判断迭代器是否有下一个要迭代的节点。当首次迭代的时候,下一个被迭代的元素应该是链表的头节点。所以我们可以虚设一个节点node,有node.next=head这一关系。next方法返回下一个迭代的元素,它的隐藏要求是,迭代器的迭代节点要向后移动一个。实现这两个方法就可以最低配的实现一个链表迭代器了。

2.2 删除指定元素remove(Object o)

       remove(Object o)方法可以删除链表中指定元素o,以下我们简称为目标元素。该方法我们的要求是要将链表中所有的目标元素,这可能会删除链表中多个元素。

       要达到这个效果的算法的方式有多种,我们将采用的算法是通过一次遍历,将链表中的所有目标元素删除,大致算法如下图所示。
删除元素
       在删除元素时,如果被删除的元素是链表头节点,那么我们直接让头节点后移,并将原有的头节点清空。

2.3 保留目标集合所有元素

       retainAll(Collection<?> c)链表中保留的元素必须是集合c中存在的元素。这个方法最简单的实现过程是通过遍历一次链表,将链表中的每个元素提取出来与集合c中的元素进行比对,如果集合c中存在该元素(可使用contains方法是进行判断),链表中的该节点保留,否则删除链表中的该元素。为了能够彻底的删除链表中所有的相同元素,我们可以调用remove(Object o)方法进行删除,该方法的实现思路如下图所示。
retainAll
       另一个方法removeAll与retainAll是极为相似的,当迭代的链表节点元素在目标集合c中,将该元素从链表中删除。

2.4 hashCode和equals

       这两个方法,我们可以暂时不考虑实现,直接借用Object的方法来实现。如果非要实现这两个方法,我们需要注意集合类在实现这两个方法的时候与普通类有些不同。

       1.大多数的集合类中的hashCode并不是唯一的(如List),它们的hash值是由集合元素所决定的,而普通类中的hashCode值应该始终保证唯一。

       2.集合类的hashCode值与equals的结果与集合中的元素高度相关,这一点不止体现在集合类中,String和Integer等封装类也是如此,在阅读它们的源码时,可以发现它们的hashCode值是由内部重要元素(变量)计算得到的。不同之处在于String等对象的值发生变化时,引用也随之变化,这一点与集合类还是不同的。

3 详细设计

       扩展后的链表命名为LinkCollection,除去原有基础链表(Link)功能外,还对接口Collection进行实现。在LinkCollection的内部,也拥有一个Node内部类,它与基本链表中的结构是一样的,本文我们忽略它的介绍。另一个内部类LinkIterator,它是迭代器的重要实现,我们将会在iterator方法中以局部内部类的方式进行创建。

3.1 LinkIterator内部类

       LinkIterator具体声明格式为"class LinkIterator implements Iterator< T >",它是创建在iterator方法中的内部类,仅在iterator方法中使用。

       LinkIterator包含一个成员变量"private Node iterNode",该变量是用来链接链表头节点的一个临时结点。初始值为"new Node(null, head)",其中"head"为链表的头节点。Iterator接口的两个覆盖方法实现过程如下:

       1. public boolean hasNext():当iterNode.next不为空时,返回true,否则返回false。
       2. punblic T next():让iterNode节点后移,返回移动后节点的元素内容。

3.2 Collection接口方法的实现

1.public Iterator iterator():创建LinkIterator对象实例并返回。
2.public boolean addAll(Collection<? extends T> c)

  • 方法描述:使用迭代器遍历集合c,将集合c中的元素加入到当前链表中。
  • 实现过程:1.先判断目标集合是否为null,为null返回false。2.目标集合和当前链表的长度之和是否大于链表的最大长度,不符合要求返回false。3.通过迭代目标集合c的方式,将集合c中的元素逐一添加到链表中,添加结束后返回true。

3.public void clear():将链表清空,重置size,head,last为初始值。
4.public boolean containsAll(Collection<?> c):

  • 方法描述:判断当前链表是否包含指定集合c中的所有元素。
  • 实现过程:1.判断目标集合是否null,为null返回false。2.用迭代器遍历目标集合c,将迭代的元素放入链表中查找(contains方法查找),如果没有找到返回false。3.迭代结束后,方法返回true。

5.public boolean isEmpty():当size等于0时返回true,否则返回false。
6.public boolean remove(Object o)

  • 方法描述:删除链表中所有与目标元素o相等的元素。
  • 实现过程:1.遍历链表中的每一个节点,遍历节点为node,注意在遍历的过程中,需要定义preNode记录node上一个节点信息。2.当node节点元素与o不相同时,node节点后移(preNode也要更新),继续迭代循环;如果node节点与元素o相同,删除该节点(如果该节点是头节点,更新头结点信息,如果该节点是尾节点,更新尾节点信息),node和preNode节点更新,继续循环遍历。3.如果有删除过节点,遍历结束后,返回true否则返回false。

7.public boolean removeAll(Collection<?> c)

  • 方法描述:删除链表中所有包含在目标集合c中的元素。
  • 实现过程:1.判断c是否为null,为null返回false。2.使用迭代器迭代目标集合c,从链表中删除迭代的元素,如果出现删除失败,返回false。3.迭代完成后,返回true。

8.public boolean retainAll(Collection<?> c)

  • 方法描述:链表中只保留在集合c中的元素。
  • 实现过程:1.判断c是否为null,为null返回false。2.遍历链表,如果遍历的节点元素不在目标集合c中,则链表删除该元素。3.遍历结束后,返回true。

9. public Object[] toArray()

  • 方法描述:将链表各节点元素转化成Object数组对象。
  • 实现过程:根据链表长度size,创建等长大小Object数组。遍历链表将,链表中各元素依次放入Object数组中,最后返回该数组。

10. public < E > E[ ] toArray(E[ ] a)

  • 方法描述:提供一个泛型数组E,将链表元素转化成类型E,并返回该数组。
  • 实现过程:这个方法中的泛型与链表的泛型是不一致的,由于泛型数组无法实例化,我们可以将链表中的元素迭代出来,依次转化后放入目标数组a中,如果数组a的长度小于链表长度,只返回a长度小大的数组,如果数组a的长度大于链表长度,则数组多余部分进行null处理。

3.2代码实现

01.	import java.util.Collection;
02.	import java.util.Iterator;
03.	
04.	public class LinkCollection<T> implements Collection<T>{
05.		
06.		private int size=0;	//链表长度
07.		private Node<T> head=null;//链表的头节点
08.		private Node<T> last=null;//链表的头节点
09.		
10.		//链表最大长度,链表长度size最大值为MAX_SIZE,是在链表中添加元素时必须判断的条件。
11.		private final int MAX_SIZE=2<<10;
12.		
13.		//链表中的节点类
14.		static class Node<T> {
15.			//链表中的节点元素
16.			T item;
17.			//下一个节点的地址
18.			Node<T> next;
19.			
20.			Node(T item,Node<T> next){
21.				this.item=item;
22.				this.next=next;
23.			}
24.		}	
25.		
26.		public boolean add(T obj){
27.			
28.			if(size>=MAX_SIZE) return false;	
29.			Node<T> newNode=new Node<T>(obj, null);	
30.			if(size==0) {
31.				//如果为头节点,首末节点等于新增节点
32.				head=newNode;
33.				last=newNode;
34.			}else {
35.				//将节点添加至尾部
36.				last.next=newNode;
37.				//尾节点为新节点
38.				last=newNode;
39.			}
40.			
41.			size++;
42.			return true;
43.		}
44.		
45.		public boolean add(int index,T obj) {
46.						
47.			if(index>=size || index<0 || size>MAX_SIZE-1) return false;		
48.			Node<T> newNode=new Node<T>(obj, null);
49.			//如果是头结点
50.			if(index==0) {
51.				newNode.next=head;
52.				head=newNode;
53.				size++;
54.				return true;
55.			}
56.			
57.			Node<T> node=head;//index索引处节点
58.			Node<T> preNode=null;//index索引之前的节点
59.			//遍历节点,node为指定序列index的链表节点,preNode为前一个节点
60.			for(int i=0;i<index;i++) {
61.				preNode=node;
62.				node=node.next;
63.			}
64.			//更新节点间的关系
65.			preNode.next=newNode;
66.			newNode.next=node;
67.			
68.			size++;
69.			return true;		
70.		}
71.		
72.		
73.		public boolean contains(Object obj) {
74.			
75.			Node<T> start=head;
76.			while(start!=null) {
77.				//当节点对象与obj的equals相等时,则找到对象
78.				if(start.item.equals(obj)) return true;
79.				//节点后移
80.				start=start.next;
81.			}
82.			
83.			return false;
84.		}
85.		
86.		
87.		public T get(int index) {
88.			
89.			if(index<0 || index>=size) return null;
90.			//从头部节点遍历到指定索引节点
91.			Node<T> node=head;
92.			for(int i=0;i<index;i++) {
93.				node=head.next;
94.			}
95.			
96.			return node.item;
97.		}
98.		
99.		public T getHead() {
100.			return head==null?null:head.item;
101.		}
102.		
103.		public T getLast() {
104.			return last==null?null:last.item;
105.		}
106.		
107.		public boolean remove(int index) {
108.			
109.			if(index>=size || index<0) return false;
110.			
111.			Node<T> removeNode=head;//被删除节点,初始值为头结点
112.			Node<T> preNode=null;//被删除节点的上一个节点
113.			//如果是头节点,将头节点后移。
114.			if(index==0) {
115.				this.head=head.next;
116.				removeNode=null;
117.				size--;
118.				return true;
119.			}
120.			//移动到指定索引节点处。
121.			for(int i=0;i<index;i++) {
122.				preNode=removeNode;
123.				removeNode=removeNode.next;
124.			}
125.			//如果被删除节点是尾节点
126.			if(removeNode.next==null) {
127.				last=preNode;
128.				preNode.next=null;
129.				removeNode=null;
130.			}else {
131.				preNode.next=removeNode.next;
132.				removeNode=null;
133.			}
134.			size--;
135.			return true;
136.		}
137.		 
138.		public int size() {
139.			return this.size;
140.		}
141.			
142.		public String toString() {
143.			StringBuilder strs=new StringBuilder();
144.			Node<T> start=head;
145.			while(start!=null) {
146.				strs.append(start.item).append(",");
147.				start=start.next;
148.			}
149.			
150.			return strs.toString();
151.		}
152.	
153.		@Override
154.		public boolean isEmpty() {
155.			return size==0?true:false;
156.		}
157.	
158.		@Override
159.		public Iterator<T> iterator() {
160.			
161.			class LinkIterator implements Iterator<T>{
162.				//相当于在链表的头结点前加一个临时结点
163.				private Node<T> iterNode=new Node<T>(null, head);
164.				@Override
165.				public boolean hasNext() {
166.					return iterNode.next==null?false:true;
167.				}
168.	
169.				@Override
170.				public T next() {
171.					iterNode=iterNode.next;
172.					return iterNode.item;
173.				}		
174.			}
175.	
176.			return new LinkIterator();
177.		}
178.	
179.		@Override
180.		public Object[] toArray() {
181.			if(size==0) return null;
182.			Object[] array=new Object[size];
183.			Iterator<T> it=this.iterator();
184.			for(int i=0;i<size;i++) {
185.				array[i]=it.next();
186.			}
187.			
188.			return array;
189.		}
190.	
191.		@SuppressWarnings("unchecked")
192.		@Override
193.		public <E> E[] toArray(E[] a) {
194.			E[] array=a;
195.			Object[] src=this.toArray();
196.			for(int i=0;i<array.length;i++) {
197.				if(i<src.length) {
198.					array[i]=(E) src[i];
199.				}else {
200.					array[i]=null;
201.				}
202.			}
203.			
204.			return array;
205.		}
206.	
207.		@Override
208.		public boolean remove(Object o) {
209.			Node<T> preNode=null;
210.			Node<T> node=head;
211.			boolean isRemove=false; //是否删除过元素标识
212.			while(node!=null) {
213.				T ele=node.item;
214.				if(ele.equals(o)) {
215.					//如果被移除节点是头结点,头结点下移
216.					if(node.equals(head)) {
217.						head=head.next;
218.					}else {
219.						preNode.next=node.next;
220.					}
221.					node=node.next;
222.					//被移除节点是尾结点
223.					if(node==null) {
224.						last=preNode;
225.					}
226.					size--;
227.					isRemove=true;
228.				}else {
229.					preNode=node;
230.					node=node.next;
231.				}
232.			}
233.			return isRemove;
234.		}
235.	
236.		@Override
237.		public boolean containsAll(Collection<?> c) {
238.			Iterator<?> it=c.iterator();
239.			while(it.hasNext()) {
240.				Object collectEle=it.next();
241.				if(!contains(collectEle)) {
242.					return false;
243.				}
244.			}
245.			return true;
246.		}
247.	
248.		@Override
249.		public boolean addAll(Collection<? extends T> c) {
250.			if(c==null) return false;
251.			if(size>MAX_SIZE-c.size()) return false;
252.			Iterator<? extends T> it=c.iterator();
253.			while(it.hasNext()) {
254.				if(!add(it.next())) {
255.					return false;
256.				}
257.			}  
258.			return true;
259.		}
260.	
261.		@Override
262.		public boolean removeAll(Collection<?> c) {
263.			if(c==null) return false;
264.			Iterator<?> it=c.iterator();
265.			while(it.hasNext()) {
266.				Object collectEle=it.next();
267.				if(contains(collectEle)) {
268.					remove(collectEle);
269.				}
270.			}
271.			return true;
272.		}
273.	
274.		@Override
275.		public boolean retainAll(Collection<?> c) {
276.			if(c==null) return false;
277.			Node<T> node=head;
278.			while(node!=null) {
279.				if(!c.contains(node.item)) {
280.					remove(node.item);
281.				}
282.				node=node.next;
283.			}
284.			
285.			return true;
286.		}
287.	
288.		@Override
289.		public void clear() {
290.			size=0;
291.			head=last=null;
292.		}
293.	}

4 链表的测试用例

       测试用例中,使用的初始化链表数据方法和链表信息输出(链表的头结点、尾节点、链表长度信息的输出)方法分别如下:

       初始化链表方法:

01.		public static Link<Integer> createLink(int size){
02.			Link<Integer> link=new Link<>();
03.			for(int i=0;i<size;i++) {
04.				link.add(i);
05.			}
06.			
07.			return link;
08.		}

       链表信息输出方法:

01.		public static void showLinkMessage(Link<?> link) {
02.			System.out.println("head="+link.getHead()
03.								+",last="+link.getLast()
04.								+",size="+link.size());
05.			System.out.println("link="+link);
06.		}

       1.addAll方法测试用例内容:正常添加集合,观察链表中元素前后的变化。

01.		public static void addAllTest() {
02.			Link<Integer> link=createLink(10);
03.			ArrayList<Integer> list=new ArrayList<>();
04.			list.add(100);
05.			list.add(200);
06.			list.add(300);
07.			showLinkMessage(link);
08.			System.out.println("addAll result="+link.addAll(list));
09.			showLinkMessage(link);
10.		}

用例运行结果:

       head=0,last=9,size=10
       link=0,1,2,3,4,5,6,7,8,9
       addAll result=true
       head=0,last=300,size=13
       link=0,1,2,3,4,5,6,7,8,9,100,200,300

       2.addAll测试用例内容:添加集合,当元素超量时,观察链表的操作反馈。

01.		public static void addAllTest2() {
02.			Link<Integer> link=createLink(2<<10);
03.			ArrayList<Integer> list=new ArrayList<>();
04.			list.add(100);
05.			list.add(200);
06.			list.add(300);
07.			System.out.println(link.size());
08.			System.out.println("addAll result="+link.addAll(list));
09.			System.out.println(link.size());
10.		}

用例运行结果:

       2048
       addAll result=false
       2048

       3.addAll测试用例内容:添加空集合时,观察链表的操作反馈。

01.		public static void addAllTest3() {
02.			Link<Integer> link=createLink(10);
03.			ArrayList<Integer> list=null;
04.			showLinkMessage(link);
05.			System.out.println("addAll result="+link.addAll(list));
06.			showLinkMessage(link);
07.		}

用例运行结果:

       head=0,last=9,size=10
       link=0,1,2,3,4,5,6,7,8,9
       addAll result=false
       head=0,last=9,size=10
       link=0,1,2,3,4,5,6,7,8,9

       4.clear测试用例内容:有数据链表清空后,观察链表前后的信息。

01.		public static void clearTest() {
02.			Link<Integer> link=createLink(10);
03.			showLinkMessage(link);
04.			link.clear();
05.			showLinkMessage(link);
06.		}

用例运行结果:

       head=0,last=9,size=10
       link=0,1,2,3,4,5,6,7,8,9,
       head=null,last=null,size=0
       link=

       5. containsAll测试用例内容:集合三种情况下进行测试。1.目标集合全部元素在链表中存在。2.目标集合部分元素在链表中存在。3.目标集合所有元素都不在链表中。分别观察方法的返回结果。

01.		public static void containsAllTest() {
02.			Link<Integer> link=createLink(10);
03.			ArrayList<Integer> list=new ArrayList<>();
04.			list.add(1);
05.			list.add(2);
06.			list.add(9);
07.			System.out.println("全部包含在link中:"+link.containsAll(list));
08.			list.add(100);
09.			System.out.println("部分包含在link中:"+link.containsAll(list));
10.			list.clear();
11.			list.add(300);
12.			list.add(400);
13.			System.out.println("不包含在link中:"+link.containsAll(list));
14.		}

用例运行结果:

       全部包含在link中:true
       部分包含在link中:false
       不包含在link中:false

       6. isEmpty方法测试用例:集合三种情况下进行测试。1.新链表,无数据时进行该方法测试。2.将链表清空后,再使用该方法测试。

01.		public static void isEmptyTest() {
02.			LinkCollection<Integer> link=new LinkCollection<>();
03.			System.out.println(link.isEmpty());
04.			link=createLink(10);
05.			System.out.println(link.isEmpty());
06.			link.clear();
07.			System.out.println(link.isEmpty());	
08.		}

用例运行结果:

       true
       false
       true

       7. removeObject方法测试用例:集合三种情况测试。1.删除头节点和重复元素。2.删除尾节点。3删除任意不重复节点。观察链表变化。

01.		public static void removeTest() {
02.			LinkCollection<Integer> link=new LinkCollection<Integer>();
03.			for(int i=0;i<20;i++) {
04.				if(i%5==0) {
05.					link.add(5);
06.				}
07.				link.add(i);
08.			}
09.			showLink(link);
10.			System.out.println("删除5:"+link.remove(new Integer(5)));
11.			showLink(link);
12.			System.out.println("删除19:"+link.remove(new Integer(19)));
13.			showLink(link);
14.			System.out.println("删除6:"+link.remove(new Integer(6)));
15.			showLink(link);
16.		}

用例运行结果:
       head=5,last=19,size=24
       link=5,0,1,2,3,4,5,5,6,7,8,9,5,10,11,12,13,14,5,15,16,17,18,19,
       删除5:true
       head=0,last=19,size=19
       link=0,1,2,3,4,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
       删除19:true
       head=0,last=18,size=18
       link=0,1,2,3,4,6,7,8,9,10,11,12,13,14,15,16,17,18,
       删除6:true
       head=0,last=18,size=17
       link=0,1,2,3,4,7,8,9,10,11,12,13,14,15,16,17,18,

       8.removeAll方法测试用例:集合两种情况测试。1.目标集合中元素部分存在与链表中。2目标集合中元素都不在链表中。

01.		public static void removeAllTest() {
02.			LinkCollection<Integer> link=new LinkCollection<Integer>();
03.			for(int i=0;i<20;i++) {
04.				if(i%5==0) {
05.					link.add(5);
06.				}
07.				link.add(i);
08.			}
09.			ArrayList<Integer> list=new ArrayList<>();
10.			list.add(5);
11.			list.add(10);
12.			list.add(19);
13.			list.add(200);
14.			showLink(link);
15.			System.out.println("删除集合"+link.removeAll(list));
16.			showLink(link);
17.			System.out.println("删除集合"+link.removeAll(list));
18.			showLink(link);
19.		}

用例运行结果:

       head=5,last=19,size=24
       link=5,0,1,2,3,4,5,5,6,7,8,9,5,10,11,12,13,14,5,15,16,17,18,19,
       删除集合true
       head=0,last=18,size=17
       link=0,1,2,3,4,6,7,8,9,11,12,13,14,15,16,17,18,
       删除集合true
       head=0,last=18,size=17
       link=0,1,2,3,4,6,7,8,9,11,12,13,14,15,16,17,18,

       9.retainAll方法测试用例:目标集合部分元素在链表中,观察该方法执行后,链表的变化。

01.		public static void retainAllTest() {
02.			LinkCollection<Integer> link=createLink(20);
03.			showLink(link);
04.			ArrayList<Integer> list=new ArrayList<>();
05.			list.add(5);
06.			list.add(10);
07.			list.add(19);
08.			list.add(200);
09.			link.retainAll(list);
10.			showLink(link);
11.		}

用例运行结果:

       head=0,last=19,size=20
       link=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
       head=5,last=19,size=3
       link=5,10,19,

       10.toArray方法测试用例:测试返回的Object数组中的数据是否正常。

01.		public static void toArrayTest1() {
02.			LinkCollection<Integer> link=createLink(20);
03.			Object[] array=link.toArray();
04.			for(Object o:array) {
05.				System.out.print(o+",");
06.			}
07.		}

用例运行结果:

       0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,

        11.toArray(E[] a) 方法测试用例:集合三种测试场景。1.提供的泛型数组与实际的链表大小一致。2.提供的泛型数组比实际的链表长。3. 提供的泛型数组比实际的链表小。

01.		public static void toArrayTest2() {
02.			LinkCollection<Integer> link=createLink(20);
03.			Object[] os=new Object[link.size()];
04.			os=link.toArray(os);
05.			Number[] ns=new Number[10];
06.			ns=link.toArray(ns);
07.			Integer[] is=new Integer[25];
08.			is=link.toArray(is);
09.			System.out.println("数组长度一致");
10.			for(Object o:os) {
11.				System.out.print(o+",");
12.			}
13.			System.out.println("\n数组长度小");
14.			for(Number n:ns) {
15.				System.out.print(n+",");
16.			}
17.			System.out.println("\n数组长度大");
18.			for(Integer i:is) {
19.				System.out.print(i+",");
20.			}
21.		}

用例运行结果:

       数组长度一致
       0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
       数组长度小
       0,1,2,3,4,5,6,7,8,9,
       数组长度大
       0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,null,null,null,null,null,

        12.iterator() 测试用例:集合两种测试场景进行测试。1.新链表的迭代测试。2.有数据的链表迭代测试。

01.		public static void iteratorTest() {
02.			LinkCollection<Integer> link=new LinkCollection<>();
03.			Iterator<Integer>  it1=link.iterator();
04.			System.out.print("新链表遍历=");
05.			while(it1.hasNext()) {
06.				System.out.print(it1.next()+" ");
07.			}
08.			System.out.print("\n有数据链表遍历=");
09.			link=createLink(10);
10.			Iterator<Integer>  it2=link.iterator();
11.			while(it2.hasNext()) {
12.				System.out.print(it2.next()+" ");
13.			}
14.		}

用例运行结果:

       新链表遍历=
       有数据链表遍历=0 1 2 3 4 5 6 7 8 9

       经过上述基本的功能测试,我们的扩展链表功能基本实现,我们可以继续增加测试用例,来检验我们编写的程序是否可靠。

5 总结

       链表是数据结构中非常重要的一种数据存储结构,在Java的Collection体系中,链表已有实现,如LinkedList就是一种经典的双向链表实现。链表在使用上是有一定的背景的,我们需要对它的特点进行一些掌握。

5.1 链表特点

       由于单向链表的特殊结构,它的遍历是非常慢的,每一次查找元素都要从链表头节点向尾节点逐一进行查找,如果被查找元素在链表的尾部,几乎需要全链表遍历,所以在单向链表中,我们就要避免对链表进行遍历。而使用链表完成先进先出(队列)和先进后出(栈)的操作。

5.2 设计问题

       从上面的测试用例来看,我们似乎已经完成了链表的设计操作。实际上它还有可能存在一些严重的问题没有被找到,这是因为我们的测试用例还不足够覆盖所有使用场景,所以导致一部分问题没有被发现。从现在设计的结构来看,它还存在如下问题。

       1.在向链表添加数据的时候,我们没有对被添加数据进行null判断。其实在一些数据结构中允许我们添加null元素,但在一些方法中就需要我们消除一些歧义,例如get(int index)方法,如果提供的索引index超出链表范围,该方法也会返回null,这就会引起歧义(是没有找到,还是找到的元素为null)。这就要我们改变一些设计,例如使用一些非法参数时,我们不使用返回null的方式进行反馈,应该使用抛出异常的方式进行反馈。

       2.清空问题,这个问题涉及到了垃圾回收问题,Java中垃圾回收器回收没有被引用的对象。当我们清除链表节点的时候,我们只是简单的将链表节点赋值了null。这样是不全面的,还应该将节点的指针域也进行清空,这样更安全一些。

       单向链表的内容我们到此结束,在下一篇中,我们会讲述双向链表的设计,并且对单向链表中遗留的一些问题进行改善。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值