Java与数据结构(一)

    之所以萌发出写这类博客的想法,源自于前不久看到的一个有关于数据结构与算法的教程,写的实在太好。可惜的是,这类教程是基于C语言来进行讲解,对于Java开发者来说可能就会有一种”屠龙之技,学而无用”的错觉。所以本系列将针对Java来讲一讲数据结构的相关知识。先附上数据结构教程,对数据结构还不是十分了解的朋友可以先学习下。

数据结构与算法教程

本系列主要讲解我们常用的数据存储类(例如:List、Set、Map等)它们的内部实现原理,全篇围绕一下3个方面展开:

1.内部存储结构
2.基本操作及原理
3.优缺点及应用场景

这是“java与数据结构”的第一篇,本篇我们来谈谈List集合。

List

List实际是一个接口,它申明了一个有序集合并提供了add()remove()set()get() 等方法对集合里的元素进行增删改查等操作。List的实现类有很多,我们来看看常见的几个实现类:ArrayList、LinkedList、Vector、Stack。
List类图
类图结构如下:
List类图结构

ArrayList

    内部存储结构顺序表(数组)

transient Object[] elementData;	

private int size;
方法描述时间复杂度
boolean add(E e)添加一个元素到集合的末尾O(1)
void add(int index, E element)插入元素到指定位置O(n)
E remove(int index)删除指定位置的元素O(n)
get(int index)获取指定位置的元素O(1)

    ArrayList内部是通过Object[]数组来存放元素的,每次向ArrayList添加元素的时候,都会先确定数组容量的大小是否满足,如果不满足,则进行容量的扩充,每次扩充的容量为原来的1.5倍。由于数组是一个连续的存储空间,所以对元素的访问数组下标直接访问,时间复杂度为O(1)。但是对于元素的插入、删除操作可能会牵涉到大量元素的整体移动,所以时间复杂度为O(n)。

LinkedList

    内部存储结构双向链表


transient Node<E> first;	//头节点

transient Node<E> last;		//尾节点

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}
方法描述时间复杂度
boolean add(E e)添加一个元素到集合的末尾O(1)
void add(int index, E element)插入元素到指定位置O(1)
E remove(int index)删除指定位置的元素O(1)
get(int index)获取指定位置的元素O(n)

    LinkedList的内部实现是双向链表,每个节点都是一个Node对象,prev指向当前元素的上一个元素,next指向当前元素的下一个元素,item就是数据域。使用双向链表的好处是:

  1. 内存空间都是动态申请的,不需要提前申请
  2. 在进行插入和删除操作时,只需要改变相应节点的指向,无需大量移动元素,所以时间复杂度为O(1)
  3. 单向链表只能通过“从前往后”的查找,而双向链表增加“从后往前”的查找功能,在访问元素上效率更高。
Node<E> node(int index) {
    // assert isElementIndex(index);

    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

Vector

    内部存储结构顺序表(数组)

 protected Object[] elementData;
 
 protected int elementCount;
方法描述时间复杂度
boolean add(E e)添加一个元素到集合的末尾O(1)
void add(int index, E element)插入元素到指定位置O(n)
E remove(int index)删除指定位置的元素O(n)
get(int index)获取指定位置的元素O(1)

Vector和ArrayList的内部实现大致相同,它们不同的地方有以下几点:

  1. Vector是线程安全的,ArrayList是线程不安全的
  2. 容量扩充上,Vector默认是扩充到原来的2倍,ArrayList默认扩充到1.5倍

如果不考虑线程安全问题,那么ArrayList显然比Vector效率更高。

Stack

    内部存储结构顺序栈

方法描述时间复杂度
E push(E item)元素入栈O(1)
E pop()元素出栈O(1)
E peek()获取栈顶元素O(1)

Stack是Vector的子类,因此,Stack的栈功能是通过数组实现的,存储过程如下:
在这里插入图片描述

总结

    以上介绍了List集合几个常用的实现类的数据结构,下面对这几个类的时间复杂度和使用场景做一个总结:

访问插入\删除线程安全使用场景
ArrayList较慢不安全访问元素较多,插入删除较少,不考虑线程安全
LinkedList不安全访问元素较少,插入删除较多,不考虑线程安全
Vector较快安全访问元素较多,插入删除较少,考虑线程安全
Stack较快安全适用于栈结构存储的数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值