1.ArrayList
我们知道,List是一个接口,而ArrayList是List接口的一个实现类.
因为接口不能直接被实例化,所以我们就可以让一个类去实现接口,然后通过让这个类实例化一个引用对象出来,那么这个对象就充当了指向List接口的对象的引用.
比如:
List<Integer> list1=new ArrayList<Integer>();
例:
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
for(int i=0;i<list.size();i++){
System.out.print(list.get(i));//获取i下标的值
}
System.out.println();
System.out.println(list.remove(2));
System.out.println(list.set(0,9));//返回值是原来i下标的值
System.out.println(list.contains(3));//判断是否包含某个元素
}
😀
List a=new ArrayList();
😍那么a就拥有了List的所有属性和方法,但是没有ArrayList独有的属性和方法.
😍a只能调用ArrayList中重写的List中的抽象方法
😍如果List和ArrayList拥有相同的属性c和方法void func().那么a.c是调用了List中的c,因为属性不会被重写.a.func()是调用了ArrayList中的func(),因为ArrayList重写了List中的func().
说明,在介绍LinkedList之前,我们先来说说ArrayList的缺陷:
ArrayList底层使用数组来存储元素,是一段连续的空间,ArrayList在任意位置插入或删除元素时,会涉及到元素的挪动,时间复杂度为O(N),效率比较低.因此,ArrayList不适合用于在任意位置插入或者删除元素较多的场景.因此,我们引入了LinkedList(无头双向链表).
2.LinkedList
首先我们要知道:LinkedList底层是无头双向链表结构.LinkedList也实现了List接口.
LinkedList中结点的构造:
由于每一个结点都有一个直接前驱和直接后继,所以双向链表里的任意一个结点都可以很方便的访问它的前驱结点和后继结点.
一个简单包含4个结点的LinkedList的结构:
😊LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问.
😊LinkedList适合任意位置插入的场景.
😊由于链表没有将元素存储在连续的空间中,而是存储在结点中,然后通过引用将结点连接起来,因此在任意位置插入或者删除元素时,不需要挪动元素(时间复杂度为O(1)),效率更高.
2.1 LinkedList的构造
双向链表是由结点组成的,我们先来看结点的组成:
public class ListNode {
public int val;
public ListNode prev;
public ListNode next;
public ListNode(int val){
this.val=val;
}
}
我们发现,每一个结点都是由三个部分组成,数值域,前驱还有后继.
接下来我们再来看双向链表(无傀儡节点)的一些基本操作:
😊链表的长度
public int size(){
ListNode cur=head;
int count=0;
while (cur!=null){
count++;
cur=cur.next;
}
return count;
}
😊打印链表
public void display(){
ListNode cur=head;
while(cur!=null){
System.out.print(cur.val+" ");
cur=cur.next;
}
System.out.println();
}
😊头插
public void addHead(int data){
ListNode node=new ListNode(data);
//如果链表为空,直接让head=last=node
if(size()==0){
head=node;
last=node;
}
//如果链表不为空
else {
node.next=head;
head.prev=node;
head=node;
}
}
😊尾插
public void addTail(int data){
ListNode node=new ListNode(data);
//如果链表为空,直接让head=last=node
if(size()==0){
head=node;
last=node;
}else{//链表不为空时
last.next=node;
node.prev=last;
last=node;
}
}
😊在index下标处插入元素
public void add(int index,int data){
ListNode node=new ListNode(data);
//先判断index是否合法
if(index<0||index>size()){
throw new IndexOutOfBoundsException("数组下标越界");
}
//如果index==0,则是头插
if(index==0){
addHead(data);
} else if (index==size()) {//尾插
addTail(data);
}else {
ListNode cur=head;
for(int i = 0; i<index; i++){
cur=cur.next;
}
cur.prev.next=node;
node.prev=cur.prev;
cur.prev=node;
node.next=cur;
}
}
😊删除头结点
public void removeHead(){
//如果链表为空,不能删除
if(size()==0){
throw new RuntimeException("链表为空,不能删除");
}
//链表不为空
else {
head=head.next;
}
}
😊删除尾结点
public void removeTail(){
//链表为空
if(size()==0){
throw new RuntimeException("链表为空,无法删除");
}else {
last=last.prev;
}
}
😊删除第一次出现的值为key的结点
public void removeKey(int key){
ListNode cur=head;
//先判断链表是否为空
if(size()==0){
throw new RuntimeException("链表为空,无法删除");
}
while (cur!=null){
if(cur.val==key){
if(cur==head){
head=head.next;
if(head!=null){
head.prev=null;
}else {
last=null;
}
}else {
if(cur.next!=null){
cur.prev.next=cur.next;
cur.next.prev=cur.prev;
}else {
cur.prev.next=cur.next;
last=last.prev;
}
}
return;
}else {
cur=cur.next;
}
}
}
😊删除所有值为key的结点
public void removeAll(int key){
if(size()==0){
throw new RuntimeException("链表为空,不能删除");
}
ListNode cur=head;
while(cur!=null){
if(cur.val==key){
if(cur==head){
head=head.next;
if(head!=null){
head.prev=null;
}else {
last=null;
}
}else {
if(cur.next!=null){
cur.prev.next=cur.next;
cur.next.prev=cur.prev;
}else {
cur.prev.next=cur.next;
last=last.prev;
}
}
cur=cur.next;
}else {
cur=cur.next;
}
}
}
😊清空链表
public void clear(){
ListNode cur=head;
while (cur!=null){
ListNode curNext=cur.next;
cur.prev=null;
cur.next=null;
cur=curNext;
}
head=null;
last=null;
}
3.整体代码
public class LinkedList2 {
public ListNode head;
public ListNode last;
//得到链表的长度
public int size(){
ListNode cur=head;
int count=0;
while (cur!=null){
count++;
cur=cur.next;
}
return count;
}
//打印链表
public void display(){
ListNode cur=head;
while(cur!=null){
System.out.print(cur.val+" ");
cur=cur.next;
}
System.out.println();
}
//头插
public void addHead(int data){
ListNode node=new ListNode(data);
//如果链表为空,直接让head=last=node
if(size()==0){
head=node;
last=node;
}
//如果链表不为空
else {
node.next=head;
head.prev=node;
head=node;
}
}
//尾插
public void addTail(int data){
ListNode node=new ListNode(data);
//如果链表为空,直接让head=last=node
if(size()==0){
head=node;
last=node;
}else{//链表不为空时
last.next=node;
node.prev=last;
last=node;
}
}
//在index下标处插入元素
public void add(int index,int data){
ListNode node=new ListNode(data);
//先判断index是否合法
if(index<0||index>size()){
throw new IndexOutOfBoundsException("数组下标越界");
}
//如果index==0,则是头插
if(index==0){
addHead(data);
} else if (index==size()) {//尾插
addTail(data);
}else {
ListNode cur=head;
for(int i = 0; i<index; i++){
cur=cur.next;
}
cur.prev.next=node;
node.prev=cur.prev;
cur.prev=node;
node.next=cur;
}
}
//删除头结点
public void removeHead(){
//如果链表为空,不能删除
if(size()==0){
throw new RuntimeException("链表为空,不能删除");
}
//链表不为空
else {
head=head.next;
}
}
//删除尾结点
public void removeTail(){
//链表为空
if(size()==0){
throw new RuntimeException("链表为空,无法删除");
}else {
last=last.prev;
}
}
//删除第一次出现的值为key的结点
public void removeKey(int key){
ListNode cur=head;
//先判断链表是否为空
if(size()==0){
throw new RuntimeException("链表为空,无法删除");
}
while (cur!=null){
if(cur.val==key){
if(cur==head){
head=head.next;
if(head!=null){
head.prev=null;
}else {
last=null;
}
}else {
if(cur.next!=null){
cur.prev.next=cur.next;
cur.next.prev=cur.prev;
}else {
cur.prev.next=cur.next;
last=last.prev;
}
}
return;
}else {
cur=cur.next;
}
}
}
//删除所有值为key的结点
public void removeAll(int key){
if(size()==0){
throw new RuntimeException("链表为空,不能删除");
}
ListNode cur=head;
while(cur!=null){
if(cur.val==key){
if(cur==head){
head=head.next;
if(head!=null){
head.prev=null;
}else {
last=null;
}
}else {
if(cur.next!=null){
cur.prev.next=cur.next;
cur.next.prev=cur.prev;
}else {
cur.prev.next=cur.next;
last=last.prev;
}
}
cur=cur.next;
}else {
cur=cur.next;
}
}
}
//清空双向链表
public void clear(){
ListNode cur=head;
while (cur!=null){
ListNode curNext=cur.next;
cur.prev=null;
cur.next=null;
cur=curNext;
}
head=null;
last=null;
}
}
4.测试代码
public class Test {
public static void main(String[] args) {
LinkedList2 linkedList2=new LinkedList2();
linkedList2.addHead(1);
linkedList2.addHead(2);
linkedList2.addHead(3);
linkedList2.display();
//3 2 1
System.out.println("==================");
linkedList2.add(0,9);
linkedList2.display();
//9 3 2 1
System.out.println("==================");
linkedList2.removeHead();
linkedList2.display();
//3 2 1
System.out.println("==================");
}
}