版权声明:本文为博主原创文章,转载请注明作者与出处,http://blog.csdn.net/lixingtao0520 https://blog.csdn.net/lixingtao0520/article/details/78359586
单向链表是指节点中的指针域只有一个,指针域只能沿着同一个方向移动的链表。Java.util包中提供了LinkedList,此LinkedList是基于双向链表实现的。本文基于单向链表实现LinkedList中的某些方法,主要是学习使用下单向链表这种数据结构。
首先给出节点类,节点类中包含数据域value,和指针域next,以及三个构造函数。
class Entry {
E value;//数据域
Entry next;//指针域
Entry() {}
Entry (E value){
this.value = value;
}
Entry (E value, Entry next){
this.value = value;
this.next = next;
}
}
为了方便处理链表中的插入,删除和查找操作,在链表的开始结点之前,增加一个head节点,此head节点指向开始结点。这样好处为:
- 开始结点的地址存放在了head节点中,这样开始结点的操作和其他节点的操作一样,无需特殊处理。
- 不论链表是否为空,其头结点的指针是指向开始节点的非空节点(只不过空链表时,头结点的指针域next为null)。空链表的处理和非空链表一样了。
我们将LinkedList类实现java.util.List<E>接口,尽可能的实现java.util.List<E>中的方法。实现的java.util.List<E>接口方法如下
返回值 | 方法 | 说明 |
boolean | add(E e) | 将特定元素e添加到List中 |
void | add(int index, E element) | 在索引index处添加给定的元素 |
boolean | addAll(Colleaction<? extends E> c) | 将集合c中的元素全部加入List中 |
boolean | addAll(intindex, Collection<?extendsE> c) | 在给定位置,添加集合c中的元素 |
int | size() | 返回链表中的元素个数 |
E | get(int index) | 获取下标为index的元素 |
boolean | isEmpty() | List为空时,返回true |
boolean | remove(Object o) | 移除链表中第一个元素值为o的元素。 |
boolean | removeAll(Collection<?> c) | 从链表中移除c中所有的元素。 |
E | remove(int index) | 删除index处的元素 |
E | set(int index, E element) | 将index处的值设置为元素element。 |
int | indexOf(Object o) |
返回元素o在链表中第一次出现的索引位置,未出现返回-1 |
boolean | contains(Object o) | 如果List中包含元素o则,返回true;否则false |
boolean | containsAll(Collection<?> c) | List中是否包含集合c中的全部元素,是返回true,否则false |
void | clear() | 清空List中的值 |
Object[] | toArray() | 将链表转换为数组,返回Object[] |
T[] | toArray(T[] a) | 将链表转换为类型为T的数组 |
int | lastIndexOf(Object o) | 返回链表中value等于o的倒数第一个索引值。 |
boolean | retainAll(Collection<?> c) | 从链表中移除所有不在集合c中的元素。 |
Iterator<E> | iterator() | 返回链表的迭代子 |
下面看下LinkedList类的具体实现。
LinkedList类成员域
LinkedList中除了有包含开始节点的head节点之外,还需要一个整型域count,此属性记录了链表中的节点个数。LinkedList类的成员域如下
。
//头指针。
private Entry head = new Entry();
//节点个数
private int count = 0;
add(E e)
向链表中插入节点时,是从开始节点插入:将新增节点指向开始节点,head指向新增节点
public boolean add(E e) {
//新增节点
Entry addE = new Entry(e);
addE.next = head.next;
head.next = addE;
count++;
return true;
}
add(int index, E element)
扫描二维码关注公众号,回复:
3414814 查看本文章
在特定位置,加入某个元素。
使用变量tmp表示当前处理的节点,变量pre表示当前节点的前趋,用i记录移动的元素个数。当i=index时,将前趋节点指针域指向新节点,新节点的指针域指向当前节点。
/*
* 特定位置加入某个元素
*/
@Override
public void add(int index, E element) {
if(index < 0 || index >= count) {
throw new RuntimeException("index越界");
}
int i = 0;
Entry tmp = head.next;
Entry pre = head;
while(i != index) {
tmp = tmp.next;
pre = pre.next;
i++;
}
Entry add = new Entry(element);
pre.next = add;
add.next = tmp;
count++;
}
addAll(Collection<?extendsE> c)
将集合c中的所有元素添加到链表中。利用集合c的迭代子循环增加元素
public boolean addAll(Collection<? extends E> c) {
Iterator<? extends E> it = c.iterator();
while(it.hasNext()) {
add(it.next());
}
return true;
addAll(intindex, Collection<?extendsE> c)
在特定位置,向链表中添加集合c中的元素
.
分两种情况:1、index<count,记录下前趋节点,将c中的元素追加到前趋节点的末尾,将追加的最后一个元素的指针域指向原index处的节点
2、index=count,即在末尾节点添加集合c中的元素。循环将c中的元素追加到末尾节点
public boolean addAll(int index, Collection<? extends E> c) {
if(index < 0 || index > count) {
throw new RuntimeException("index越界");
}
/*
* 如果index等于count大小,需要单独处理
*/
if(index == count) {
Entry tmp = head;
while(null != tmp.next) {
tmp = tmp.next;
}
//循环将c中的元素追加到末尾
Iterator<? extends E> it = c.iterator();
while(it.hasNext()) {
Entry add = new Entry(it.next());
tmp.next = add;
tmp = tmp.next;
count++;
}
return true;
}
/*
* 如果index < count,则按以下逻辑处理。
* 记录下前趋节点,移动到index处时,将c中的元素追加到前趋节点末尾
*/
int i = 0;
Entry tmp = head.next;
Entry pre = head;
while(i != index) {
tmp = tmp.next;
pre = pre.next;
i++;
}
//循环将c中的元素追加到pre的末尾
Iterator<? extends E> it = c.iterator();
while(it.hasNext()) {
Entry add = new Entry(it.next());
pre.next = add;
pre = pre.next;
count++;
}
//将pre的指针域指向原index处的节点tmp
pre.next = tmp;
return true;
}
size()
返回链表中的元素个数
public int size() {
return count;
}
get(int index)
获取下标为index的元素。
1 通过head指针,循环去访问后继元素
2 因为插入时,是从开始结点插入的。获取时,后插入的先获取(栈)
/*
* 获取下标为index的元素
*/
public E get(int index) {
if(index < 0 || index >= size()){
throw new RuntimeException("IndexOutOfBounds");
}
//获取头指针
Entry headTemp = head;
//开始节点
Entry target = headTemp.next;
int i = 0;
while(i != index) {
target = target.next;
i++;
}
return target.value;
}
isEmpty()
空链表判断
public boolean isEmpty() {
return size() == 0;
}
remove(Object o)
移除链表中第一个元素值为o的元素。
记下当前节点的前趋节点,循环判断,如果移除的o与value相同,则将当前前趋节点的next指向
当前节点的next.
public boolean remove(Object o) {
Entry tmp = head.next;
if(null == tmp || null == o) {
return false;
}
//tmp的前趋节点
Entry pre = head;
while(null != tmp) {
Object v = tmp.value;
if(o.equals(v)) {
pre.next = tmp.next;
count--;
return true;
}
pre = tmp;
tmp = tmp.next;
}
return false;
}
remove(intindex)
删除index处的元素
public E remove(int index) {
if(index < 0 || index >= count) {
throw new RuntimeException("index越界");
}
//开始节点
Entry tmp = head.next;
int i = 0;
while(i != index) {
tmp = tmp.next;
i++;
}
return tmp.value;
}
removeAll(Collection<?> c)
从链表中移除c中所有的元素。
利用集合c的迭代子,循环移除
public boolean removeAll(Collection<?> c) {
Iterator<?> it = c.iterator();
while(it.hasNext()) {
boolean re = remove(it.next());
if(re != true) {
return false;
}
}
return true;
}
set(int index, E element)
将index处的值设置为元素element。
public E set(int index, E element) {
if(index < 0 || index >= size()){
throw new RuntimeException("IndexOutOfBounds");
}
//获取头指针
Entry headTemp = head;
//开始节点
Entry target = headTemp.next;
int i = 0;
while(i != index) {
target = target.next;
i++;
}
E oldValue = target.value;
target.value = element;
return oldValue;
}
indexOf(Object o)
获取某个值的索引。
元素o为null和不为null时分开处理。
public int indexOf(Object o) {
Entry tmp = head.next;
int index = 0;
if(o == null) {//o为null
while(tmp != null && tmp.value != null) {
tmp = tmp.next;
index++;
}
} else {//o不为null
while(tmp != null && !o.equals(tmp.value)) {
tmp = tmp.next;
index++;
}
}
if(null == tmp) {
return -1;
} else {
return index;
}
}
contains(Object o)和containsAll(Collection<?> c)
链表中是否包含某个元素
public boolean contains(Object o) {
return indexOf(o) != -1;
}
链表中是否包含集合c中的所有元素
public boolean containsAll(Collection<?> c) {
Iterator<?> it = c.iterator();
while(it.hasNext()) {
boolean re = contains(it.next());
if(re != true) {
return false;
}
}
return true;
}
clear()
清空链表。只是让头节点指向了null。内存中真正的节点,并为消失
public void clear() {
head.next = null;
count = 0;
}
toArray()和toArray(T[] a)
将链表转换为数组
public Object[] toArray() {
Object[] arr = new Object[count];
Entry tmp = head.next;
int i = 0;
while(null != tmp) {
arr[i] = tmp.value;
tmp = tmp.next;
}
return arr;
}
将链表转换为类型为T的数组,如果给定的a长度不够,则将重新实例化一个T数组。
public <T> T[] toArray(T[] a) {
//如果a的长度不够,则重新实例化一个数组
if (a.length < count)
a = (T[])java.lang.reflect.Array.newInstance(
a.getClass().getComponentType(), count);
Object[] re = a;
Entry tmp = head.next;
int i = 0;
while(null != tmp) {
re[i] = tmp.value;
i++;
tmp = tmp.next;
}
//多出部分填充为null
if(a.length > count) {
a[count] = null;
}
return a;
}
lastIndexOf(Object o)
返回链表中value等于o的倒数第一个索引值。
逻辑:将链表向后移动判断每个value值,并使用一个变量lastIndex存储value与o最后一次相等的索引地址,当链表移动到末尾时,如果lastIndex值为-1,说明链表中不存在value为o的节点,
否则,返回lastIndex的值。o为null时和不为null时,分开处理
public int lastIndexOf(Object o) {
Entry tmp = head.next;
int index = 0;
int lastIndex = -1;
if(o == null) {
while(tmp != null) {
if(tmp.value != null) {
tmp = tmp.next;
index++;
} else {
lastIndex = index;
tmp = tmp.next;
index++;
}
}
} else {
while(tmp != null) {
if(!o.equals(tmp.value)) {//如果元素o与value不相等,则继续向后移动。
tmp = tmp.next;
index++;
} else {//如果元素o与value相等,则记录下此处的索引,继续向后移动,寻找是否还有值与o相等。
lastIndex = index;
tmp = tmp.next;
index++;
}
}
}
if(-1 == lastIndex) {
return -1;
} else {
return lastIndex;
}
}
retainAll(Collection<?> c)
从链表中移除所有不在集合c中的元素。
public boolean retainAll(Collection<?> c) {
Entry tmp = head.next;
Entry pre = head;//tmp的前趋节点,移除时使用
while(null != tmp) {
E value = tmp.value;
if(!c.contains(value)) {//如果c中不包含value,则移除对应的节点,并将tmp指向tmp.next,pre指针不动。
pre.next = tmp.next;
count--;
tmp = tmp.next;
continue;
}
//如果c中包含value,则将tmp指向tmp.next,pre指向tmp。
pre = tmp;
tmp = tmp.next;
}
return true;
}
iterator()
获取链表的迭代子
public Iterator<E> iterator() {
class Itr implements Iterator<E> {
//迭代类中,所处理元素的当前索引位置。起始为-1
private int cur = -1;
@Override
public boolean hasNext() {
return cur + 1 < size();
}
//获取迭代类中当前索引的元素
@Override
public E next() {
cur++;
E tmp = get(cur);
return tmp;
}
@Override
public void remove() {
//必须调用过next()方法
if(cur < 0) {
throw new RuntimeException("cur<0");
}
//移除cur处的元素
Entry tmp = head.next;
Entry pre = head;
int i = 0;
while(cur != i && tmp != null) {//cur与i不相等且没有移动到末尾
tmp = tmp.next;
pre = pre.next;
i++;
}
//当移动到当前处理的节点cur时,进行移除处理
if(cur == i) {
if(tmp != null) {
//移除的为非末尾节点
pre.next = tmp.next;
count--;
cur--;//迭代子处理的元素位置减1
return;
} else {
//移除的为末尾节点
pre.next = null;
count--;
cur--;//迭代子处理的元素位置减1
return;
}
} else {
throw new RuntimeException("找不到remove的元素");
}
}
}
return new Itr();
}