在链式存储中,我们每创建一个对象,其中所存的东西都分为,数据部分和指针部分。指针部分保存该对象的下一个节点对象,即下一个对象的地址,在Java里面保存地址的我们称之为引用,在C语言里面我们称之为指针。
我们可以通过该引用去指向下一个对象,同样下一个对象也有自己的指针部分。每当我们要存一个元素到这个集合里头时,都要创建一个新的节对象。结构与前面的对象相同,并且通过指针部分将其连接在其中。
如果我们要进行下面的操作,则
访问对象
首先我们要拿到链表中第一个对象节点的地址,持有第一个对象节点的引用,有时候也称其为头指针head。我们找到这个"头"之后,通过头指针的引用找到第一个对象,后面的对象也就找到了。并且可以对目标对象的数据进行操作。
因为链表是挨个访问,所以相对于数组,添、删、改是其优势,对象访问是其短板。
链表中,对象的添、删、改只需要创建一个新的对象,然后对该对象的指针进行修改
添加
删除
链表中,对象的访问是需要一个一个遍历的,而数组中,只要给出了索引就可以直接跳过前面的元素,找到相应位置的元素。
现在有一个问题是,我们利用节点来添、删、改、查数据时,也是有些麻烦的。那么能不能有更简便的方法来解决这个问题?
封装
对于用户来说,我们应该把最原始的数据封装起来,不让他们知道里面的情况。
看如下代码:
/**
* 链式存储(已封装)
*/
public class LinkedList {
//只要使用链表,我们就需要节点
//我们可以将这个节点放到类的内部,使其私有化。
//从而成为内部类。
//而写到类的内部的原因,
//是因为这个内部类旨在链表里边用,外边没有用。
//它只是作为链表类的节点,对于用户来说是不需要知道的。
private class Node {
private int data;
private Node next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
//创建链表,需要创建头结点引用。且开始为空。
private Node head = null;
private int size = 0;
//我们要做的以下操作是在链表里进行的
/**
* 添
* @param d
*/
public void add(int d) {
//创建节点
Node n = new Node();
n.setData(d);
//判断头结点是否为空
if (head == null)
head = n;
else {
//临时变量
Node temp = head;
//遍历
//这里要注意的是,如果只有一个元素,
//我们一次都不要循环,这样加第二次的时候,
//直接给temp赋值。
for (int i = 0; i <<
4000
/span> size - 1; i++) {
//temp始终指向当前元素的下一个元素
temp = temp.getNext();
}
//当这里一次都不循环时,
//我们就给头结点所指向的对象的next直接进行赋值。
//当循环结束指向最后一个元素的时候,
//我们就给指向头结点的对象的next进行赋值(添入新的数据)即可。
temp.setNext(n);
}
//当出了块之后,这里面声明的变量就没了。
size++;
}
/**
* 取
* @param i
* @return
*/
public Integer get(int i) {
if (i == 0){
return head.getData();
}
Node temp = head;
for (int t = 0; t < i; t++) {
temp = temp.getNext();
if (temp == null) {
//如果temp为空,说明就没有第i个节点。
//如果不判断,就会报空指针异常。
return null;
}
}
//若为返回空,则说明到了第i个元素了。
return temp.getData();
}
}
public class 集合的概念{
public static void main(String[] args) {
/**
*链式存储:
* 添加(未封装)
*/
//创建头结点
Node head = new Node();/*head:Node@496 此为哈希码,且哈希码是惟一的*/
//头结点数据域,存入一个数据。
head.setData(20);/*head:Node@496*/
//head.setNext();// 头结点指针域,目前什么都没有。
//因为我们要存的不只是一个节点,所以要拿到下一个节点。
//head.getNext();//因为此时next为空,
//所以head指向的是刚刚new出来的Node。
//而head也拿到了Node里的所有数据,
//其中就包括set方法。
//同时,next域的指针是空的,
// 我们可以在给其new一个新的节点。
Node n = new Node();/*n:Node@497*/
//此时n节点是独立的,目前和head节点还没有关系。
//给其赋值
n.setData(50);/*n:Node@497*/
//赋值给next,
//让next等于n要连接的下一个节点的地址。
//其实next引用类型还是Node,
//实际上就是通过set方法,
//把n的地址交给了head里面的next引用。
head.setNext(n);/*head:Node@496 n:Node497*/
n = new Node();/*n:Node@497*/
//到这里,head可以访问自己的数据,
//同样也可以访问下一个节点里的数据,
//这就说明,这两个节点都在同一个即合里了。
n.setData(60);/*n:Node@498*/
head.getNext().setNext(n);
/*head:Node@496 n:Node498*/
//在这里,新节点的next总是空的。
//如此重复
//......
//取数据
System.out.println(head.getNext().getNext().getData());
/**
* 添加(链式存储 已封装)
*/
LinkedList bb=new LinkedList();
bb.add(20);
bb.add(222);
bb.add(233);
/**
* 取(链式存储 已封装)
*/
//注意,这里面的用法和ArrayList一样,
//但是结构不一样。
System.out.println(bb.get(2));
}
}
class Node {
private int data;//数据部分
private Node next;//节点部分
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}