Java数组和链表的区别
从数据结构上说,数组的内存空间是连续的,我们创建数组的时候系统就会为我们开辟固定数目的内存空间,如果内存不足,就会创建失败,例如创建数组的两种方式: int[] a=new int[3];
int[] b=new int[]{1,2,3};
可以看到我们创建数组的时候已经指定了数组的大小,且不能动态更改数组的大小,是因为创建时候已经分配了连续的固定内存空间,每个元素占用两个字节,这样我们就可以通过连续的内存,去访问数组的元素;
链表的内存分配是动态的,链表的元素占用的空间包含元素占用的空间,还有指向上一个或者下一个元素的指针(双链表,单链表);
这样我们可以得出各自的优缺点:
数组链表的优缺点:
数组占用空间小,链表元素还要包涵上一元素和下一个元素的的信息
数组的访问速度快,因为内存是连续的
数组内部元素可以随机访问,而链表依赖于上一个元素的信息
链表的插入删除操作由于数组,因为内存不连续,只需要更改元素的前后节点信息就行了,并不需要更改元素内存地址,而数组的连续内存想要插入和删除的话就要移动所有的内存地址
链表的内存利用率高于数组,链表内存是分散的一个元素占用一块空间,数组元素少于内存空间的话,会有部分的内存浪费;
链表的扩展性强,数组的创建完成内存大小就确定了,满了就没法扩展只能再次创建新的数组,而链表可以随意的增加扩展
效率:数组查询效率高,链表增,删效率高
下面是个单链表的实现:
public class LinkList<T> {
public class Node{ //定义节点
private T data;
private Node next;
public Node(){
}
public Node(T data,Node next){
this.data=data;
this.next=next;
}
}
private Node header;//头节点
private Node tail;//尾节点
private int size;//链表大小
//构造函数初始化数据
public LinkList(){
header=null;
tail=null;
}
public LinkList(T element){
header=new Node(element,null);
tail=header;
size++;
}
//采用尾插法为链表添加新节点
private void add(T element) {
if(header==null){
header=new Node(element,null);
tail=header;
}else{
Node newNode=new Node(element,null);
tail.next=newNode;
tail=newNode;
}
size++;
}
//在index位置插入节点element
public void insert(T element,int index){
if(index<0||index>size){
throw new IndexOutOfBoundsException("线性表索引越界");
}
if(header==null){
add(element);
}else{
if(index==0){
addAtHeader(element);
}else{
Node prev=getNodeByIndex(index-1);
prev.next=new Node(element,prev.next);
size++;
}
}
}
//采用头插法为链表添加新节点
private void addAtHeader(T element){
header=new Node(element,header);
if(tail==null){
tail=header;
}
size++;
}
//返回指定位置index的节点
public T get(int index){
return getNodeByIndex(index).data;
}
private Node getNodeByIndex(int index) {
if(index<0||index>size-1){
throw new IndexOutOfBoundsException("线性表索引越界");
}
Node current=header;
for(int i=0;i<size;i++,current=current.next){
if(i==index){
return current;
}
}
return null;
}
//链表长度
public int length(){
return size;
}
//返回element在链表的位置,-1表示不存在
public int locate(T element){
Node current=header;
for(int i=0;i<size;i++,current=current.next){
if(current.data==element){
return i;
}
}
return -1;
}
//删除index位置的节点
public T delete(int index){
if(index<0||index>size-1){
throw new IndexOutOfBoundsException("线性表索引越界");
}
Node del=null;
if(index==0){
del=header;
header=header.next;
}else{
Node prev=getNodeByIndex(index-1);
del=prev.next;
prev.next=del.next;
del.next=null;
}
size--;
return del.data;
}
//从链表后面删除一个节点
public T remove(){
return delete(size-1);
}
//是否为空
public boolean empty(){
return size==0;
}
//清空链表
public void clear(){
header=null;
tail=null;
size=0;
}
public String toString(){
if(empty()){
return "[]";
}else{
StringBuilder sb=new StringBuilder("[");
for(Node current=header;current!=null;current=current.next){
sb.append(current.data.toString()+", ");
}
int len=sb.length();
return sb.delete(len-2, len).append("]").toString();
}
}
public static void main(String[] args) {
LinkList<String> list=new LinkList<String>();
list.insert("1", 0);
list.add("2");
list.add("3");
System.out.println(list.toString());
list.insert("4", 1);
System.out.println(list.toString());
list.delete(2);
System.out.println(list.toString());
System.out.println("1在链表中的位置:"+list.locate("1"));
System.out.println("链表中索引2处的元素:"+list.get(2));
}
}