java顺序表和链表

1.什么是集合框架

早在 Java 2 中之前,Java 就提供了特设类。比如:Dictionary, Vector, Stack, 和 Properties 这些类用来存储和操作对象组。

虽然这些类都非常有用,但是它们缺少一个核心的,统一的主题。由于这个原因,使用 Vector 类的方式和使用 Properties 类的方式有着很大不同。

集合框架被设计成要满足以下几个目标。

  • 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。

  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。

  • 对一个集合的扩展和适应必须是简单的。

为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedListHashSet, 和 TreeSet 等,除此之外你也可以通过这些接口实现自己的集合。

从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayListLinkedListHashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。

集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:

  • 接口:是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象

  • 实现(类):是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。

  • 算法:是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序,这些算法实现了多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。

集合框架体系图

1. Collection:是一个接口,包含了大部分容器常用的一些方法

2. List:是一个接口,规范了ArrayList 和 LinkedList中要实现的方法

    ArrayList:实现了List接口,底层为动态类型顺序表

     LinkedList:实现了List接口,底层为双向链表

3. Stack:底层是栈,栈是一种特殊的顺序表

4. Queue:底层是队列,队列是一种特殊的顺序表

5. Deque:是一个接口

6. Set:集合,是一个接口,里面放置的是K模型

     HashSet:底层为哈希桶,查询的时间复杂度为O(1)

     TreeSet:底层为红黑树,查询的时间复杂度为O(\log_{2}N)关于key有序的

7. Map:映射,里面存储的是K-V模型的键值对 ),关于key有序的

     HashMap:底层为哈希桶,查询时间复杂度为O(1)   

      TreeMap:底层为红黑树,查询的时间复杂度为O(\log_{2}N ),关于key有序

2.Arraylist

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。

ArrayList 继承了 AbstractList ,并实现了 List 接口。

说明

1. ArrayList是以泛型方式实现的,使用时必须要先实例化

2. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问

3. ArrayList实现了Cloneable接口,表明ArrayList是可以clone的

4. ArrayList实现了Serializable接口,表明ArrayList是支持序列化的

5. 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者 CopyOnWriteArrayList

6. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表

2.1Arraylist的使用

2.1.1Arraylist中方法的调用

ArrayList 类位于 java.util 包中,使用前需要引入它,语法格式如下:

import java.util.ArrayList; // 引入 ArrayList 类

ArrayList<E> objectName =new ArrayList<>();  // 初始化

  • E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型
  • objectName: 对象名。

注意

其他的引用类型

ArrayList 中的元素实际上是对象,在以上实例中,数组列表元素都是字符串 String 类型。

如果我们要存储其他类型,而 <E> 只能为引用数据类型,这时我们就需要使用到基本类型的包装类。

基本类型对应的包装类表如下:

ArrayList常见操作

1.ArrayList add() 方法

arraylist.add(int index,E element)
  • index(可选参数)- 表示元素所插入处的索引值
  • element - 要插入的元素

如果 index 没有传入实际参数,元素将追加至数组的最末尾。

返回值

如果成功插入元素,返回 true。

注意:如果 index 超出范围,则该 add() 方法抛出 IndexOutOfBoundsException 异常。

ArrayList<Integer> primeNumbers = new ArrayList<>();
primeNumbers.add(2);
 primeNumbers.add(1)
primeNumbers.add(5);

2. ArrayList addAll() 方法

addAll() 方法将给定集合中的所有元素添加到 arraylist 中

arraylist.addAll(int index, Collection c)

注:arraylist 是 ArrayList 类的一个对象。

参数说明:

  • index(可选参数)- 表示集合元素插入处的索引值
  • c - 要插入的集合元素

如果 index 没有传入实际参数,元素将追加至数组的最末尾。

返回值

如果成功插入元素,返回 true。

如果给定的集合为 null,则超出 NullPointerException 异常。

注意:如果 index 超出范围,则该方法抛出 IndexOutOfBoundsException 异常。

ArrayList<Integer> primeNumbers = new ArrayList<>();
primeNumbers.add(3);
 primeNumbers.add(5);
ArrayList<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
numbers.addAll(primeNumbers);

3.ArrayList clear() 方法

clear() 方法用于删除动态数组中的所有元素。

4.ArrayList clone() 方法

clone() 方法用于拷贝一份动态数组,属于浅拷贝。

拓展:

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存, 所以如果其中一个对象改变了这个地址,就会影响到另一个对象。。

浅拷贝对应的就是深拷贝,深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

5.ArrayList contains() 方法

contains() 方法用于判断元素是否在动态数组中。

6.ArrayList get() 方法

get() 方法通过索引值获取动态数组中的元素。

7.ArrayList indexOf() 方法

ndexOf() 方法返回动态数组中元素的索引值。

8.ArrayList removeAll() 方法

emoveAll() 方法用于删除存在于指定集合中的动态数组元素。

9.ArrayList remove() 方法

remove() 方法用于删除动态数组里的单个元素。

语法形式

// 删除指定元素
arraylist.remove(Object obj)

// 删除指定索引位置的元素
arraylist.remove(int index)

如果 obj 元素出现多次,则删除在动态数组中最第一次出现的元素。

10.ArrayList size() 方法

size() 方法用于返回动态数组中元素的数量。

11.ArrayList isEmpty() 方法

isEmpty() 方法用于判断动态数组是否为空。

12.ArrayList subList() 方法

subList() 方法用于截取并返回动态数组中的一部分。

arraylist.subList(int fromIndex, int toIndex)
  • fromIndex - 截取元素的起始位置,包含该索引位置元素
  • toIndex - 截取元素的结束位置,不包含该索引位置元素

13.ArrayList set() 方法

set() 方法用于替换动态数组中指定索引的元素

语法形式

arraylist.set(int index, E element)
  • index - 索引位置
  • element - 将在 index 位置替换进去的新元素

14.ArrayList sort() 方法

sort() 方法根据指定的顺序对动态数组中的元素进行排序。

语法形式

arraylist.sort(Comparator c)
  • comparator - 顺序方式

15.ArrayList toArray() 方法

toArray() 方法将 Arraylist 对象转换为数组。

16.ArrayList toString() 方法

toString() 方法将 Arraylist 对象转换为字符串。

17.ensureCapacity()

方法用于设置具有指定容量大小的动态数组。

18.ArrayList lastIndexOf() 方法

lastIndexOf() 方法返回指定元素在动态数组中最后一次出现的位置。

19.ArrayList retainAll() 方法

etainAll() 方法用于保留 arraylist 中在指定集合中也存在的那些元素,也就是删除指定集合中不存在的那些元素。

20.ArrayList containsAll() 方法

ontainsAll() 方法用于检测 arraylist 是否包含指定集合中的所有元素

21.ArrayList trimToSize() 方法

trimToSize() 方法用于将动态数组中的容量调整为数组中的元素个数。

22.ArrayList removeRange() 方法

removeRange() 方法用于删除指定索引之间存在的元素。

语法形式

arraylist.removeRange(int fromIndex, int toIndex)
  • fromIndex - 索引起始位置,包含该索引位置的值
  • toIndex - 索引结束位置,不包含该索引位置的值

23.ArrayList replaceAll() 方法

placeAll() 方法用于将给定的操作内容替换掉数组中每一个元素。

24.ArrayList removeIf() 方法

removelf() 方法用于删除所有满足特定条件的数组元素。

在以上实例中,我们使用了 Java String contains() 方法来检查元素中是否包含 "Tao"。

e -> e.contains("land") 如果元素中包含 "land",则返回 true

removeIf() 如果 e -> e.contains("land") 返回 true 则删除该元素。

25.ArrayList forEach() 方法

orEach() 方法用于遍历动态数组中每一个元素并执行特定操作。

2.1.2Arraylist的遍历

ArrayList 可以使用三方方式遍历:for循环+下标、foreach、使用迭代器

 List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    list.add("4");
    list.add("5");
    // 使用下标+for遍历
    for (int i = 0; i < list.size(); i++) {
        System.out.print(list.get(i) + " ");
    }
    System.out.println();
    // 借助foreach遍历
    for (Integer integer : list) {
        System.out.print(integer + " ");
    }
    System.out.println();
    Iterator<Integer> it = list.listIterator();
    while(it.hasNext()){
        System.out.print(it.next() + " ");
    }
    System.out.println();

// 获取ArrayList的迭代器
Iterator<String> iterator = list.iterator();

// 使用hasNext()检查是否还有更多元素
while(iterator.hasNext()) {
    // 使用next()获取下一个元素
    String element = iterator.next();
    
    // 在这里可以对element进行操作
    System.out.println(element);
    
    // 注意:如果你想在遍历过程中删除元素,应该使用iterator的remove()方法,而不是直接调用list的remove()方法
    // 示例:如果满足某条件,则删除元素
    // if(某些条件) {
    //     iterator.remove();
    // }
}

2.2Arraylist底层代码实现

(参数是基本数据类型int,不是引用数据类型)

底层代码构成

三个类

1.Test类(实现并测试完成的MyAraylist代码)

public class Test {
    public static void main(String[] args) {
        //实现一个Arrayslist,创建一个类

        ILst lst=new MyArrayList();
        lst.add(1);
        lst.add(2);
        System.out.println(lst.get(1));
        // lst.display();


    }
}

2.MyArraylist类(包含参数和方法)

import java.util.Arrays;

public class MyArrayList implements   ILst {


    //顺序表的底层代码是数组,所以定义参数有两个一个是数组,一个是优先数据的个数
    public int[] array;
    public int usedsize;//有效数据


    //定义一个常数用于初始化数组的大小
    private static final int DEFAULT_CAPCITY =3 ;

    //调用这个类时,需要对数组的大小进行初始化,有效数据就不需要
    public MyArrayList() {
        this.array = new int[DEFAULT_CAPCITY];
    }

    //关于Arraylist的方法就用一个接口

    //扩容方法
    private void Expension(){
        //将数组的容量增加一倍
        this.array= Arrays.copyOf(this.array,2*array.length);
    }

    //增加元素对pos位置检查方法
    private void Examine1(int pos){
        if(pos<0||pos>this.usedsize){
            throw new AddposException("pos位置有问题");
        }
    }
    @Override
    public void add(int data) {
        // 新增元素,默认在数组最后新增

        //1.首先要对数组的容量的大小进行判断
        if(usedsize==array.length){
            //封装一个方法对数组进行扩容
            Expension();
        }
            //2.在对元素进行添加
            this.array[this.usedsize]=data;
            this.usedsize++;
    }

    @Override
    public void add(int pos, int data) {
        // 在 pos 位置新增元素
        //有两个注意点
        //第一点需要判断传进来的pos位置是否合法,对于第一点可以创建一个自定义的异常来对pos进行检查
        //第二点也是要进行扩容

        try{
            //先判断pos位置是否合法
            Examine1(pos);
            //pos位置没有问题的时候
            //增容操作
            if(usedsize==array.length ){
                Expension();
            }
            for(int j=this.usedsize-1;j>=pos;j--){
                this.array[j+1]=this.array[j];
            }
            //再将data值给插入进来
            this.array[pos]=data;
            this.usedsize++;
        }catch(AddposException e){
            //结束程序运行
            e.printStackTrace();
        }
    }

    @Override
    // 判定是否包含某个元素
    public boolean contains(int toFind) {
        for(int i=0;i<usedsize-1;i++){
            if (array[i]==toFind) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int indexOf(int toFind) {
        // 查找某个元素对应的位置
        for(int i=0;i<usedsize-1;i++){
            if(array[i]==toFind){
                return i;
            }
        }
        //没有找到返回-1
        return -1;
    }
    private void Examine2(int pos ){
        if(pos<0||pos>=usedsize){
            throw new GetposException("pos位置不合法");
        }
    }
    @Override
    public int get(int pos) {
        // 获取 pos 位置的元素
        //还是需要检查pos位置是否合法,此时不能调用之前的pos位置的检查方法,因为此时的pos位置的数不能==usedsize
        try {
            Examine2(pos);
            //下面是pos位置合法的情况
            return this.array[pos];
        }catch(GetposException e){
            e.printStackTrace();
        }

        return 0;
    }

    private void Examine3(int pos){
        if(pos<0||pos>=this.usedsize){
            throw new SetposException("pos位置不合法");
        }
    }
    @Override
    public void set(int pos, int value) {
        // 给 pos 位置的元素设为 value
        //有pos之前就先要检查pos的位置是否合法,pos位置不能==usedsize
        try{
            Examine3(pos);
            //下面是pos位置合法的情况下
            this.array[pos]=value;

        }catch(SetposException e){
            e.printStackTrace();
        }
    }

    @Override
    public void remove(int toRemove) {
        //删除第一次出现的关键字key
        for(int i=0;i<usedsize-1;i++){
            if(this.array[i]==toRemove){
                for(int j=i;j<usedsize-1;j++){
                    this.array[j]=this.array[j+1];
                }
                usedsize--;
                return;
            }
        }
    }

    @Override
    public int size() {
        // 获取顺序表长度
        return usedsize;
    }

    @Override
    public void clear() {
        // 清空顺序表
        //对于基本数据类型的话,直接将usedsize置为空就行,胆识如果是引用数据类型就需要将数组的每个位置都置为nell
        this.usedsize=0;
    }

    @Override
    public void display() {
        for (int i = 0; i < this.usedsize; i++) {
            System.out.print(array[i] + " ");
        }
    }

}

3.SetposException类(自定义异常类用于报错Set方法中pos位置不合法的情况)

public class SetposException extends RuntimeException{
    public SetposException(String message) {
        super(message);
    }


}

4.GetposException类(自定义异常类用于报错get方法中pos位置不合法的情况)

public class GetposException extends RuntimeException {

    public GetposException(String message) {
        super(message);
    }


}

5.AddposException类(自定义异常类用于报错Add方法中pos位置不合法的情况)

public class AddposException extends  RuntimeException{
    public AddposException(){

    }

    public AddposException(String message) {
        super(message);
    }

}

一个接口

6.ILst接口(用于声明MyArraylist中的方法)

public interface ILst {
    //接口的方法必须全都被重写

    // 新增元素,默认在数组最后新增
    public void add(int data);
    // 在 pos 位置新增元素
    public void add(int pos, int data) ;
    // 判定是否包含某个元素
    public boolean contains(int toFind) ;
    // 查找某个元素对应的位置
    public int indexOf(int toFind);
    // 获取 pos 位置的元素
    public int get(int pos);
    // 给 pos 位置的元素设为 value
    public void set(int pos, int value);
    //删除第一次出现的关键字key
    public void remove(int toRemove);
    // 获取顺序表长度
    public int size() ;
    // 清空顺序表
    public void clear() ;
    // 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
    public void display();
}
 

3.LinkedList

3.1LinkedList使用

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。

链表可分为单向链表和双向链表。

一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接。

一个双向链表有三个整数值: 数值、向后的节点链接、向前的节点链接。

Java LinkedList(链表) 类似于 ArrayList,是一种常用的数据容器。

与 ArrayList 相比,LinkedList 的增加和删除的操作效率更高,而查找和修改的操作效率较低。

以下情况使用 ArrayList :

  • 频繁访问列表中的某一个元素。
  • 只需要在列表末尾进行添加和删除元素操作。

以下情况使用 LinkedList :

  • 你需要通过循环迭代来访问列表中的某些元素。
  • 需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。

LinkedList 继承了 AbstractSequentialList 类。

LinkedList 实现了 Queue 接口,可作为队列使用。

LinkedList 实现了 List 接口,可进行列表的相关操作。

LinkedList 实现了 Deque 接口,可作为队列使用。

LinkedList 实现了 Cloneable 接口,可实现克隆。

LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

3.2LinkedList中方法的使用

1.addFirst

在列表开头添加元素

2. addLast()

在列表结尾添加元素:

3.removeFirst()

移除头部元素

4.removeLast()

移除尾部元素

5. getFirst()

获取头部元素

6.public boolean add(E e)

链表结尾处添加元素,返回是否成功,成功返回true,失败返回false

7.public void add(int index, E element)

向指定位置插入元素

8.public boolean addAll(Collection c)

将整个集合插入在链表的结尾,成功返回true,失败返回false

9.public boolean addAll(int index, Collection c)

将一个集合的元素插入到指定的位置,成功返回true,失败返回false

10.public boolean offer(E e)

向链表末尾添加元素,返回是否成功,成功为 true,失败为 false。


11.public boolean offerFirst(E e)

头部插入元素,成功返回true,失败返回false

12.public boolean offerLast(E e)

尾部插入元素,返回是否成功,成功为 true,失败为 false。

13.public void clear()

清空链表

14.public boolean remove(Object o)

删除某一个元素,成功返回true,失败返回false

15.public E remove(int index)

删除指定位置的元素,成功返回true,失败返回false

16.public E poll()

删除并返回第一个元素

17.public E remove()

删除并返回第一个元素


18.public boolean contains(Object o)

判断是否含有某一个元素

19.public E get(int index)

返回指定位置的元素

20.public E getFirst()

返回第一个元素

21.public E getLast()

返回最后一个元素

22.public int indexOf(Object o)

查找指定元素,从前往后返回第一次出现的索引值

23.public int lastIndexOf(Object o)

查找指定元素,最后一次的索引值

24.public E peek()

返回第一个元素

25.public E element()

返回第一个元素

26.public E peekFirst()

返回头部元素

27.public E peekLast()

返回尾部元素

28.public E set(int index, E element)

设置指定位置的元素

29.public Object clone()

克隆此链表

30.public Iterator descendingIterator()

返回倒序迭代器

31.public int size()

返回链表元素个数

32.public ListIterator listIterator(int index)

返回指定位置到末尾的迭代器

33.public Object[] toArray()

返回一个由链表元素转换类型而成的数组。

3.3SingleLinkedList

(无头单向非循环链表)底层代码实现

(参数是基本数据类型int,不是引用数据类型)

两个类

1.Test类(测试底层代码方法的类)

public class Test {
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList=new SingleLinkedList();
        singleLinkedList.chuangjian();
        singleLinkedList.display();
        System.out.println();
        System.out.println(singleLinkedList.size());
        System.out.println(singleLinkedList.contains(1));
        System.out.println(singleLinkedList.contains(9));
        singleLinkedList.addFirst(5);
        singleLinkedList.addLast(6);
        singleLinkedList.display();
        System.out.println();
        singleLinkedList.addIndex(1,7);
        singleLinkedList.display();
        System.out.println();
        singleLinkedList.remove(5);
        singleLinkedList.remove(6);
        singleLinkedList.remove(2);
        singleLinkedList.display();

    }
}

2.SingleLinkedList类(实现底层代码)

{
    //链表是由一个一个节点组成的,我们就可以将一个一个的节点抽象成一个一个的内部类
    static class  ListNode{
        //一个节点由两部分组成
        public int val;//本节点的值
        public ListNode next;//下一个结点的地址

        public ListNode(int val) {
            this.val = val;
        }

        @Override
        public String toString() {
            return "ListNode{" +
                    "val=" + val +
                    '}';
        }
    }
    public ListNode head;

    //头插法
    @Override
    public void addFirst(int data) {

        //先对数据先申请一块空间
        ListNode listNode=new ListNode(data);
        listNode.next=head;
        head=listNode;

    }
    //尾插法
    @Override
    public void addLast(int data) {
        ListNode listNode=new ListNode(data);
        ListNode cur=head;
        //特殊情况,当没有节点时head==null
        if(head==null){
            head=listNode;
            head.next=null;
            return ;
        }
        while(cur.next!=null){
            cur=cur.next;
        }
        cur.next=listNode;
        listNode.next=null;
    }

    //创建一个函数用于判断index位置是否合法
    public void Examine(int index){
        if(index<0||index>this.size()){
            throw new IndexException("index位置有问题");
        }
    }

    //任意位置插入,第一个数据节点为0号下标
    @Override
    public void addIndex(int index, int data) {

        //创建一个自定义异常
        try{
           // 第一步需要判断位置是否是合法的
            Examine(index);

            //判断插入的位置
            //1.有可能是头插
            if(index==0){
                this.addFirst(data);
                return;
            }
            //2.有可能是尾插
            if(index==this.size()){
                this.addFirst(data);
                return ;
            }
            //3.中间插入
            ListNode listNode=new ListNode(data);
            ListNode cur=head;
            //中间插入的话,只需要找到插入位置的前一个结点就行
            while(index!=1){
                cur=cur.next;
                index--;
            }
            //此时cur是我们要找的前一个节点
            listNode.next=cur.next;
            cur.next=listNode;

        }catch(IndexException e){
            e.printStackTrace();
        }


    }
    //查找key关键字是否在链表当中
    @Override
    public boolean contains(int key) {
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }
    //删除第一次出现关键字为key的节点

    @Override
    public void remove(int key) {
        //相同的删除key值就找的前一个节点
        //当key值在最后时也满足这
        //当head==null时
        if(head==null){
            return;
        }
        //如果是头删
        if(head.val==key){
            head=head.next;
            return ;
        }
        ListNode cur=head;
        while(!(cur.next.val==key)){
            //当链表中没有key值时
            if(cur.next==null){
                return ;
            }
            cur=cur.next;
        }
        cur.next=cur.next.next;

    }
    //删除所有值为key的节点

    @Override
    public void removeAllKey(int key) {

        //当节点为空的时候
        if(head==null){
            return;
        }

        //使用双指针解决
        ListNode cur=head.next;
        ListNode prive=head;
        while(cur!=null){
           //当cur是我们要删除节点
            if(cur.val==key){
                prive.next=cur.next;
                cur=cur.next;
            }else{
                prive=cur;
                cur=cur.next;
            }
        }
        //这个是解决当头节点也是要删除的对象时的情况
        if(head.val==key){
            head=head.next;
        }
    }

    //返回节点的个数
    @Override
    public int size() {
        ListNode cur=head;
        int count=0;
     while(cur!=null){
         count++;
         cur=cur.next;
     }
     return count;
    }

    @Override
    public void clear() {

        ListNode cur=head;
        while(cur!=null){
            ListNode newcur=cur.next;
            cur.next=null;
            cur=newcur;
        }
        head=null;

    }
    public void chuangjian(){
        ListNode listNode=new ListNode(1);
        ListNode listNode2=new ListNode(2);
        ListNode listNode3=new ListNode(3);
        ListNode listNode4=new ListNode(4);
        listNode.next=listNode2;
        listNode2.next=listNode3;
        listNode3.next=listNode4;
        listNode4.next=null;
        this.head=listNode;
    }

    //打印链表
    @Override
    public void display() {
        //cur为了遍历链表的时候不影响head
        ListNode cur=head;
        while(cur!=null){
            System.out.print(cur.val+" ");
            cur=cur.next;
        }

    }



    public static void main(String[] args) {

    }
}

一个接口

1.IList(用于声明SingleLinkedList中的方法)、

public interface IList {
    //头插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);
    //删除第一次出现关键字为key的节点
    public void remove(int key);
    //删除所有值为key的节点
    public void removeAllKey(int key);
    //得到单链表的长度
    public int size();
    //清空链表
    public void clear() ;
    //打印链表
    public void display() ;

}

一个自定义的异常

1.indexException(自定义类用于对pos位置报错)

public class IndexException extends RuntimeException{
    public IndexException(String message) {
        super(message);
    }



}

3.4LinkedList

无头双向链表非循环链表底层代码实现

(参数是基本数据类型int,不是引用数据类型)

两个类

1.Test类(测试底层代码方法的类)

public class Test {
    public static void main(String[] args) {
        //测试链表打印
        MyLinkedList myLinkedList=new MyLinkedList();
        myLinkedList.jianzhao();
        myLinkedList.display();
        //测试链表个数
        System.out.println(myLinkedList.size());
        //测试contains方法
        System.out.println(myLinkedList.contains(11));
        //测试头插法
        myLinkedList.addFirst(10);
        myLinkedList.display();
        //测试尾插法
        myLinkedList.addLast(15);
        myLinkedList.display();
        //测试指定位置插入元素
        myLinkedList.addIndex(0,9);
        myLinkedList.addIndex(6,16);
        myLinkedList.addIndex(2,3);
        myLinkedList.display();
        //测试删除链表当中第一个索引值的指定元素
        myLinkedList.remove(9);
        myLinkedList.remove(16);
        myLinkedList.remove(3);
        myLinkedList.display();
        //测试删除链表中所有指定元素
        myLinkedList.addIndex(2,77);
        myLinkedList.addFirst(77);
        myLinkedList.addLast(77);
        myLinkedList.display();
        myLinkedList.removeAllKey(77);
        myLinkedList.display();
        //测试清空链表方法
        myLinkedList.clear();
        myLinkedList.display();
    }
}

2.MyLinkedList(实现底层代码)

public class MyLinkedList implements IList {
    public static class ListNode {
        int val;//存入本节点的值
        ListNode next;//存入下一个结点的值
        ListNode prev;//存入上一个节点的值

        public ListNode(int val) {
            this.val = val;
        }
    }

    //定义一个头节点
    public ListNode head;
    //定义一个尾节点
    public ListNode last;

    //头插法
    @Override
    public void addFirst(int data) {
        ListNode newnode=new ListNode(data);
        //当head为空的时候
        if(head==null){
            head=newnode;
            last=newnode;
        }
        //当head不为空的时候
        head.prev=newnode;
        newnode.next=head;
        head=newnode;


    }
    //尾插法
    @Override
    public void addLast(int data) {
        ListNode newnode=new ListNode(data);
        if(last==null){
            last=newnode;
            head=newnode;
        }
        //不为空的情况下
        last.next=newnode;
        newnode.prev=last;


    }
    public void judgment(int index){
        if(index<0||index>=this.size()){
            throw new IndexException("index位置有问题");
        }
    }
//指定位置插入元素
    @Override
    public void addIndex(int index, int data) {

        //第一步判断插入的位置是否合法
        try{
            judgment(index);
            //如果是头插调用头插
            if(index==0){
                this.addFirst(data);
                //插入完返回
                return;
            }
            //如果是尾插调用尾插
            if(index==this.size()-1){
                this.addLast(data);
                return ;
            }
            //如果是中间部分
            ListNode newnode=new ListNode(data);
            //首先要找到插入的位置
            ListNode cur=head;
            int sop=index;
            while(sop!=0){
                cur=cur.next;
                sop--;
            }
            //找到了位置
            newnode.prev=cur.prev;
            newnode.next=cur;
            cur.prev.next=newnode;
            cur.prev=newnode;
        }catch(IndexException e){
            e.printStackTrace();
        }


    }

    //查找单链表中是否包含key关键元素
    @Override
    public boolean contains(int key) {
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }

    //删除链表第一个索引值的指定元素
    @Override
    public void remove(int key) {
        //当链表为空的时候
        if(head==null){
            return;
        }
        //先找到要删除的元素
        ListNode cur=head;
        while(cur.val!=key){
            cur=cur.next;
            //加一个判断,用于链表中根本就没有指定元素的时候
            if(cur==null){
                return;
            }
        }
        //当链表只有一个节点的时候
        if(cur.prev==null&&cur.next==null){
            head=last=null;
            return;
        }
        //如果删除的是头节点的话
        if(cur.prev==null){
            cur.next.prev=null;
            head=cur.next;
            return ;
        }
        //如果删除的是尾节点的话
        if(cur.next==null){
            last=cur.prev;
            cur.prev.next=null;
            return;
        }
        //如果删除的是中间元素的话
        cur.prev.next=cur.next;
        cur.next.prev=cur.prev;

    }
    //删除链表当中所有的指定元素
    @Override
    public void removeAllKey(int key) {
        //当链表为空的时候
        if(head==null){
            return;
        }
        ListNode cur=head;
        ListNode curn=head.next;
        while(cur!=null){
            //先进行判断是否是指定元素
            if(cur.val==key){
                //然后进行判断指定元素的位置
                //当链表只有一个节点的时候
                if(cur.prev==null&&cur.next==null){
                    head=last=null;
                    return;
                }
                //如果删除的是头节点的话
                if(cur.prev==null){
                    cur.next.prev=null;
                    head=cur.next;
                    cur=curn;
                    curn=curn.next;
                    continue;
                }
                //如果删除的是尾节点的话
                if(cur.next==null){
                    last=cur.prev;
                    cur.prev.next=null;
                    return;

                }
                //如果删除的是中间元素的话
                cur.prev.next=cur.next;
                cur.next.prev=cur.prev;

            }
            cur=curn;
            curn=curn.next;
        }



    }

    @Override
    public int size() {
        ListNode cur=head;
        int count=0;
        while(cur!=null){
            count++;
            cur=cur.next;
        }
        return count;
    }

    //打印链表
    @Override
    public void display() {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }

    //清空链表
    @Override
    public void clear() {
        ListNode cur=head;
        while(cur!=null){
            ListNode curn=cur.next;
            cur=null;
            cur=curn;
        }
        head=last=null;

    }

    //创建有个LinkedList链表实例
    public void jianzhao() {
        ListNode node1 = new ListNode(11);
        ListNode node2 = new ListNode(12);
        ListNode node3 = new ListNode(13);
        ListNode node4 = new ListNode(14);
        node1.next = node2;
        node2.prev=node1;
        node2.next=node3;
        node3.prev=node2;
        node3.next=node4;
        node4.prev=node3;
        //头尾赋值
        head=node1;
        last=node4;
    }
}

一个接口

1.IList(用于声明MyLinkedList中的方法)

public interface IList{
    //无头双向链表实现

    //头插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);
    //删除第一次出现关键字为key的节点
    public void remove(int key);
    //删除所有值为key的节点
    public void removeAllKey(int key);
    //得到单链表的长度
    public int size();
    //打印链表
    public void display();
    //清空链表
    public void clear();
}

一个自定义的异常

1.indexException(自定义类用于对pos位置报错)

public class IndexException extends RuntimeException {
    public IndexException(String message) {
        super(message);
    }
}

4.ArrayList与LinkedList的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值