写在前面
如果让你保存多个对象,你第一时间想到的肯定是数组,但是如果让你保存多个任意对象呢?这时我们会想到用Object型的数组来解决。
Object[] data = new Object[3];
但是数组是一个长度固定的线性结构,在实际开发中,不论我们的内容不足或过多,都有可能造成空间的浪费,所以我们可以使用一个链表来动态存储数据,就像火车车厢的设计,长度不固定,有多少数据就保存多少数据。
单链表:
class Node {
//节点中数据
private Object data;
//指向下一个节点
private Node next;
public Node(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
public class SingleLinkedList {
public static void main(String[] args) {
Node head = new Node("火车头");
Node first = new Node("1号车厢");
Node second = new Node("2号车厢");
Node last = new Node("火车尾");
head.setNext(first);
first.setNext(second);
second.setNext(last);
printLink(head);
}
public static void printLink(Node node) {
if (node != null) {
System.out.println(node.getData());
node = node.getNext();
printLink(node);
}
}
}
思考:链表和数组的区别
- 数组是由下标索引和data组成,链表是由data和指向下一个节点的next组成
- 数组静态分配内存,链表动态分配内存
- 数组在内存中连续,链表不连续
- 数组元素在栈区,链表元素在堆区
- 数组随机访问性强(时间复杂度:O(1)),查找速度快,但是插入删除效率低(时间复杂度:O(n));
链表插入删除速度快(时间复杂度:O(1)),但是不能随即查找(时间复杂度:O(n)),必须从第一个开始遍历,查找效率低。
在实际开发中,我们用的更多的是双向链表。那么双向链表与单向链表的区别又是啥呢?其实双向链表只是在查找前一个节点的时候进行了优化,时间复杂度变为了O(1)。
双向链表:
在单链表里,挂载在客户端实现,类比生活中坐火车,我们只是乘坐,而不需要把火车的车厢连在一起,那么如何解决呢?
在双向链表里有一个新的类Link
:负责节点之间的动态挂载,Node
负责节点内容的设置,真实保存内容,ILink
表示如果Link类的结构发生了变化,就需要更改客户端代码,为了避免这种情况,就让用户直接使用ILink接口。
interface ILink {
/**
* 链表增加节点操作
*
* @param data 节点内容
* @return
*/
boolean add(Object data);
/**
* 删除指定内容节点
*
* @param data
* @return
*/
boolean remove(Object data);
/**
* 判断指定内容节点在链表中是否存在
*
* @param data 要判断的内容
* @return 返回指定的索引
*/
int contains(Object data);
/**
* 根据指定下标修改节点内容
*
* @param index 索引下标
* @param newData 替换后的内容
* @return 替换之前的内容
*/
Object set(int index, Object newData);
/**
* 根据指定下标返回节点内容
*
* @param index
* @return
*/
Object get(int index);
/**
* 链表清空
*/
void clear();
/**
* 将链表转为数组
*
* @return 返回所有节点内容
*/
Object[] toArray();
/**
* 链表长度
*
* @return
*/
int size();
/**
* 遍历链表
*/
void printLink();
}
class LinkImpl implements ILink {
private Node head;
private Node last;
private int size;
/**
* 用户并不知道Node类的存在,封装为内部类
* 真正的火车车厢,负责数据存储
* 内外部类随便访问私有属性
*/
private class Node {
private Node prev;
private Object data;
private Node next;
public Node(Node prev, Object data, Node next) {
this.prev = prev;
this.data = data;
this.next = next;
}
}
public boolean add(Object data) {
//尾插
Node temp = this.last;
Node newNode = new Node(temp, data, null);
this.last = newNode;
if (this.head == null) {
this.head = newNode;
} else {
temp.next = newNode;
}
this.size++;
return true;
}
public boolean remove(Object data) {
if (data==null){
for (Node temp=head;temp!=null;temp=temp.next){
if(temp.data==null){
unLink(temp);
return true;
}
}
}else {
for (Node temp=head;temp!=null;temp=temp.next){
if (temp.data.equals(data)){
unLink(temp);
return true;
}
}
}
return false;
}
public int contains(Object data) {
//传进来的data可能为空,不能用equals()进行比较
if (data==null){
int i=0;
for (Node temp=this.head;temp!=null;temp=temp.next){
if (temp.data==null){
return i;
}
i++;
}
}else {
int i=0;
for (Node temp=this.head;temp!=null;temp=temp.next){
if (temp.data.equals(data)){
return i;
}
i++;
}
}
return -1;
}
public Object set(int index, Object newData) {
if (!isLinkedIndex(index)){
return null;
}
Node node = node(index);
Object elementData = node.data;
node.data=newData;
return elementData;
}
public Object get(int index) {
if(!isLinkedIndex(index)){
return null;
}
return node(index).data;
}
public void clear() {
for (Node temp = head; temp != null; ) {
temp.data = null;
Node node = temp.next;
temp = temp.prev = temp.next = null;
temp = node;
this.size--;
}
}
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node temp = head; temp != null; temp = temp.next) {
result[i++] = temp.data;
}
return result;
}
public int size() {
return this.size;
}
public void printLink() {
Object[] data = this.toArray();
for (Object temp : data) {
System.out.println(temp);
}
}
//根据指定索引取得具体的节点
private Node node(int index) {
//从前往后查找
if(index<(size>>1)){
Node temp = this.head;
for(int i=0;i<index;i++){
temp=temp.next;
}
return temp;
}
Node temp=this.last;
for(int i=size-1;i>index;i--){
temp=temp.prev;
}
return temp;
}
/**
* 判断指定索引是否合法
*/
private boolean isLinkedIndex(int index){
return index>=0 && index<size;
}
/**
* 删除节点
*/
private Object unLink(Node x){
Object elementData = x.data;
Node prev = x.prev;
Node next = x.next;
if(prev==null){
this.head=next;
}else {
prev.next=next;
x.prev=null;
}
if(next==null){
this.last=prev;
}else {
next.prev=prev;
x.next=null;
}
x.data = null;
this.size--;
return elementData;
}
}
public class DoubleLinkedList {
public static void main(String[] args) {
ILink link = new LinkImpl();
link.add("火车头");
link.add("1号车厢");
link.add("2号车厢");
link.add("3号车厢");
link.add("车厢尾");
System.out.println(link.contains("2号车厢"));
System.out.println(link.contains("哈哈哈"));
System.out.println(link.get(2));
System.out.println(link.set(1,"未知车厢"));
System.out.println("————————————");
link.printLink();
System.out.println("————————————");
link.add(null);
System.out.println(link.remove("2号车厢"));
System.out.println(link.remove(null));
// link.clear();
// System.out.println("车厢已清空!");
}
}