Java中的链表

1.链表

链表简介:
是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序
实现的。链表由一系列结点组成,结点可以运行时动态生成。
链表分类:单向链表、双向链表、循环链表
单向链表:每个结点包含两个部分,一部分是存储数据元素的数据域,另一部分存储下一个结点地址的
指针域。
双向链表:每个结点包含三个部分,一部分是存储数据元素的数据域,一部分存储上一个结点地址的指
针域,一部分存储下一个结点地址的指针域。
优点:克服数组需要预先知道数据大小的缺点。可以充分利用计算机内存空间,实现灵活的内存动态管
理。由于链表不必须按顺序存储,链表在插入的时候可以达到 O(1) 的复杂度。
缺点:查找速度非常慢,每次查找元素,都要从头结点开始进行查找,或者采用二分查找的方式,从尾
结点进行查找,效率很低。链表的结点,需要存储相邻结点的指针域,所以空间开销比较大。

2.自定义链表

package com.coder.link;

import java.util.NoSuchElementException;

/**
 * 一个简单的泛型链表实现
 */
public class MyLinkedList<E> {

    // 结点
    private class Node<E> {
        Node<E> next;     // 指向后继结点
        E data;            // 数据元素
        Node<E> previous;  // 指向前驱结点

        public Node(Node<E> next, E data, Node<E> previous) {
            this.next = next;
            this.data = data;
            this.previous = previous;
        }
    }

    // 头结点
    private Node<E> first;

    // 尾结点
    private Node<E> last;

    // 有效元素个数
    private int size;

    /**
     * 默认构造函数
     */
    public MyLinkedList() {
    }

    public MyLinkedList(Node<E> first, Node<E> last, int size) {
        this.first = first;
        this.last = last;
        this.size = size;
    }

    /**
     * 将元素加到链表的尾部
     */
    public boolean add(E e) {
        Node<E> l = last;               // 将尾结点赋值给l
        Node<E> newNode = new Node<>(null, e, l);  // 新创建一个结点
        last = newNode;                // 将新创建的结点置成尾结点

        if (l == null) {
            first = newNode;           // 如果没有尾结点,则新创建的结点就是第一个结点
        } else {
            l.next = newNode;
        }

        size++;
        return true;
    }

    /**
     * 将元素插入到指定索引位置
     */
    public void add(int index, E e) {
        // 要在链表头部插入数据
        if (index == 0) {
            first = new Node<>(first, e, null);
        } else {
            // 获取上一个结点
            Node<E> prev = node(index - 1);
            // 获取下一个结点
            Node<E> next = prev.next;
            Node<E> newNode = new Node<>(next, e, prev);
            prev.next = newNode;
        }
        size++;
    }

    private Node<E> node(int index) {
        // 优化,判断查找的index和size的关系
        Node<E> x = first;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        return x;
    }

    /**
     * 获取指定索引位置的元素
     */
    public E get(int index) {
        return node(index).data;
    }

    /**
     * 获取链表的大小
     */
    public int size() {
        return size;
    }

    /**
     * 获取链表的头结点元素值
     */
    public E getFirst() {
        if (first == null) {
            throw new NoSuchElementException();
        }
        return first.data;
    }

    /**
     * 获取链表的尾结点元素值
     */
    public E getLast() {
        if (last == null) {
            throw new NoSuchElementException();
        }
        return last.data;
    }

    /**
     * 移除指定索引位置的元素
     */
    public E remove(int index) {
        Node<E> node = first;
        if (index == 0) {
            first = node.next;
        } else {
            Node<E> prev = node(index - 1);
            node = prev.next;
            prev.next = node.next;
        }
        size--;
        return node.data;
    }
}

这是一个Java类 MyLinkedList,它实现了一个简单的链表数据结构。以下是对这个类的代码和功能的分析:

  1. 类结构:
  • 这个类实现了一个泛型链表,可以存储任何类型的元素。
  • 类中有一个内部私有类 Node,表示链表中的节点,包括数据元素 data、指向下一个节点的引用 next,以及指向前一个节点的引用 previous
  1. 成员变量:

    • first: 表示链表的头结点。
    • last: 表示链表的尾结点。
    • size: 表示链表中有效元素的个数。
  2. 构造函数:
  • 类有两个构造函数,一个是默认构造函数,另一个接受头结点、尾结点和有效元素个数的参数。
  1. 添加元素:

    • add(E e): 将元素加到链表的尾部,创建一个新的节点并将其链接到尾部。
    • add(int index, E e): 将元素插入到指定索引位置,根据索引找到前一个节点和后一个节点,然后插入新节点。
  2. 获取元素:

    • get(int index): 获取指定索引位置的元素。
    • getFirst(): 获取链表的头结点元素。
    • getLast(): 获取链表的尾结点元素。
  3. 移除元素:
    • remove(int index): 移除指定索引位置的元素,根据索引找到前一个节点和待删除节点,然后移除。

这个类实现了一个简单的链表数据结构,提供了基本的添加、获取和移除元素的功能。它可以用于存储任何类型的数据,但在实际使用中,可能需要进一步扩展以满足特定的需求。此外,注释中提到了一些与 LinkedList 相关的内容,但这些内容不在这个类的代码中。

3.LinkedList

底层使用链表数据结构
新增方法
addFirst(Object) :将给定元素插入链表的开头
addLast(Object) :将给定元素追加到链表的尾部
getFirst() :获取链表的头结点元素值
getLst() :获取链表的尾结点元素值
removeFirst() :移除链表的头结点,并返回其中的元素值
removeLast(): 移除链表的尾结果,并返回其中的元素值

4.ArrayListLinkedList比较

存储结构:
ArrayList: 底层是数组结构,线性顺序存储
LinkedList :底层是链表结构,非连续,非顺序的存储,对象间是依靠指针域串连起来
操作性能:
ArrayList: 适合随机查询数据的操作
LinkedList :适合元素的插入删除操作

5.Vector向量

ArrayList 处理方式底层都是使用数组结构完成
比较:
  1. ArrayList在构造方法时,创建的大小为0,当第一次加入元素时,进行扩容。Vector在构造方法时,创 建的大小为10.
  2. ArrayList每次扩容都是原有大小的1.5倍,Vector扩容时,如果给定了capacityIncrement,则新的数 组大小为原有数组大小+capacityIncrement,否则扩容为原有大小的2
  3. ArrayList非线程安全,Vector是线程安全的。

6.哈希表

散列表,也叫哈希表,是根据键和值而直接进行访问的数据结构,可以不经过任何比较,一次直接从表
中得到要搜索的元素。通过一种函数,使用元素的存储位置和它的键值建立一个映射关系,加快查找的
速度,这个函数就叫做哈希函数或散列函数,存放记录的数组就叫做哈希表。
由于哈希函数设计问题,可能会产生哈希冲突。
哈希函数:
除留余数法 ( 常用 )
直接定址法 ( 常用 )
平方取中法
折叠法
随机数法
数学分析法
哈希函数设计的越精妙合理,产生哈希冲突的可能性就越低,但是无法避免哈希冲突
解决哈希冲突的问题:闭散列、开散列
闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中还有空位置,所
以,使用该方法,会找下一个空位置。使用线性探测,从发生冲突的位置开始,依次向后探测,直到寻
找到下一个空位置为止。
开散列:也叫链地址法或开链法,先是通过散列函数计算关键码,具有相同地址的关键码归于同一个子
集合,每一个子集合称为一个桶 ( 哈希桶 ) ,各个桶中的元素通过一个单链表链接起来,各链表的头结点存
储在哈希表中
如果出现了极端情况,所有的数据都冲突到一个桶中,将链表改成红黑树结构

7.HashSet

底层使用 HashMap ,将数据存储到 hashMap key 上面
特点:无序性,唯一性。
如果存储的数据具有相同的 hashcode ,则会调用 equals 方法再次进行比较
常用方法和 List 接口相同
没有 List 接口中的索引处理方式

8.LinkedHashSet

HashSet 的子类
底层是一个 LinkedHashMap ,维护了一个数组 + 双向链表
LinkedHashSet 是根据元素 HashCode 值来决定元素的存储位置,同时,使用链表维护元素的次序,使
用元素看起来是以插入顺序保存的。
LinkedHashSet 中维护的双向链表,每一个节点,都有 before after 属性,在添加一个元素时,先求
hash 值,再求索引,确定这个元素在 hashtable 中的位置,然后再将元素加入到双向链表。在遍历时,
可以确保和插入的顺序相一致。
不可以出现重复元素。
特性:有序性,唯一性。

9.TreeSet

底层数据结构是二叉树
在放入数据时,会根据二叉树算法,将数据进行排列
采用中序遍历,将数据读取出来。所以对于 TreeSet 来讲,不管放入元素的顺序是什么样的,读取出来 时,都是以升序排列。
TreeSet 泛型里的对象,是要具有排序能力的 (Comparable) 。如果没有实现 Comparable 接口的话,则需 要在TreeSet 构造方法中,传入 Comparator 接口的对象

10.排序接口

在做对象的比较排序时,使用两种方式,一种是 Comparable 接口,一种是 Comparator 接口。
使用 Comparable 接口的处理方式,让类实现接口,重写 compareTo() 方法,对其中的某个属性进行大 小的比较,小于返回负数,相等返回0 ,大于返回正数 使用Comparator 接口的处理方式,创建一个接口的实现类,重写 compare() 方法(传入两个对象),根 据业务需求,对两个对象的属性进行比较。将实现类的对象放在TreeSet 构造方法中。相当于一个外部的 比较器,比较灵活,耦合度更低。更多的情况,是可以使用匿名内部类或lambda 表达式的方式实现。
如果业务逻辑是固定的,就是按照特定的属性进行排序处理,可以使用 Comparable 接口。如果业务逻 辑不固定,使用Comparator 接口来实现。

11.Collections工具类

sort(list) :按升序排列
sort(list,Comparator): 按外部比较器规则进行排序
reverse(list) :按降序排列
shuffle(list) :随机排序
swap(list,int,int) :交换两个索引位置的元素
max(list) :获取集合中的最大值
min(list) :获取集合中的最小值
binarySearch(list Object) :二分查找,返回索引( list 必须是有序的)
fill(list,Object) :填充,用指的值替换 list 中的值
replaceAll(list,Object,Object) :将 list 集合中旧元素换成新的元素
frequency(collection,Object) :统计元素出现的次数
rotate(list,int) 旋转,如果第 2 个参数为 0 ,则没有改变,如果为正数,则将 list 集合最后的几位移动到集 合前面,如果为负数,则将list 集合前几位元素移动到后面 线程同步的处理:
synchronizedCollection(collection)
synchronizedMap(map)
synchronizedList(list)
synchronizedSet(set)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

师范大学通信大怨总

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值