线性表ArrayList和LinkedList源码详解。
List
描述
线性表抽象接口,所有线性表应该在实现这个接口的基础上进行操作。
接口
package list;/*** Description: 线性表的接口,使用泛型保证类型** @ClassName: List* @author 过道* @date 2018年8月13日 上午10:45:13*/ public interface ListInterface<T> {public void add(T newEntry);public void add(Integer newPosition, T newEntry);public T remove(int givePosition);public void clear();public T replace(int givenPosition, T newEntry);public T getEntry(int givenPosition);public T[] toArray();public boolean contains(T anEntry);public int getLength();public boolean isEmpty(); }
ArrayList
1 接口
/*** 为了区分java默认的 ArrayList和我的list,所以命名为AList* 因为内部没有实现,所以很简单,就没有写注释*/ public class AList<T> implements ListInterface<T> {@Overridepublic void add(T newEntry) {}@Overridepublic void add(Integer newPosition, T newEntry) {}@Overridepublic T remove(int givePosition) {return null;}@Overridepublic void clear() {}@Overridepublic T replace(int givenPosition, T newEntry) {return null;}@Overridepublic T getEntry(int givenPosition) {return null;}@Overridepublic T[] toArray() {return null;}@Overridepublic boolean contains(T anEntry) {return false;}@Overridepublic int getLength() {return 0;}@Overridepublic boolean isEmpty() {return false;}}
2.实现途径
Array的意思就是数组,所以很简单可以猜测到这是使用数组实现List,为了知道数组中多少个有效位,使用一个int值保存。初始化时,要么用户给定数组容量,要么使用默认容量。
注:强烈建议在使用ArrayList或其他双倍扩容的容器时给定容量,因为这将大大节省扩容的时间。
public class AList<T> implements ListInterface<T> {//空数组,用来存放数据T[] list = null;// 当前有效的数字个数int numberOfEntries = 0;//如果用户不给定容量(强烈建议初始化时给定容量), 默认的数组容量static final int DEFAULT_CAPACITY = 27;@SuppressWarnings("unchecked")public AList(int initialCapacity) {T[] tempList = (T[]) new Object[initialCapacity];list = tempList;numberOfEntries = 0;}public AList() {this(DEFAULT_CAPACITY);}// 其余覆盖的方法 }
实现add方法
注意一下,我们使用下标从1开始,所以不要与ArrayList混淆(从0开始)。
注:ArrayList数组使用的是 1.5 倍扩容,即如果当前数组空间已满,我们申请双倍容量的数组,然后将原数组copy到新数组中,所以扩容是比较慢的。
Vector使用的是2倍扩容,不过我这里为了省事,使用了2倍扩容。
@Overridepublic void add(T newEntry) {// 与ArrayList 不同,我们选择下标从 1 开始。list[numberOfEntries + 1] = newEntry;numberOfEntries++;ensureCapacity();//也可以选择重用其他方法 // add(numberOfEntries+1,newEntry); // 这样重用,能有效减少代码,但是理解上稍微有些困难。并且增加了许多无谓的判断,降低了效率(当然这无所谓) }/*** 指定位置进行添加某一个数字** @param newPosition* @param newEntry*/@Overridepublic void add(Integer newPosition, T newEntry) {if ((newPosition >= 1) && (newPosition <= numberOfEntries + 1)) {if (newPosition <= numberOfEntries) {makeRoom(newPosition); // 将给定位置的数字及后续数字全部后移。 }list[newPosition] = newEntry;numberOfEntries++;ensureCapacity(); // 为下次添加获取足够的空间} else {throw new IndexOutOfBoundsException("给定位置不合法,下标越界");}}/*** @param newPosition*/private void makeRoom(Integer newPosition) {assert (newPosition >= 1) && (newPosition <= newPosition + 1); //断言,如果不满足断言,则报错,需要手动开启assertint newIndex = newPosition;int lastIndex = numberOfEntries;for (int index = 0; index < newIndex; index++) {list[index + 1] = list[index]; //给定位置之后的数字全部后移一位,将给定位置‘空’下来 }}/*** 倍扩容量*/private void ensureCapacity() {int capacity = list.length - 1;if (numberOfEntries >= capacity) {int newCapacity = 2 * capacity;list = Arrays.copyOf(list, newCapacity + 1);}}
实现简单方法
只要看懂了前面的底层实现,那么对于下面三个方法的实现应该不会有问题。
@Overridepublic int getLength() {return numberOfEntries;}@Overridepublic boolean isEmpty() {return numberOfEntries == 0;}@Overridepublic void clear() {list = null; //取消引用,等待GC回收numberOfEntries = 0;}
remove方法
数组移除一个元素后,那么后续元素立刻跟进,弥补空隙。
注意:已经扩容后的容量不会被返还,也没有必要返还。
如果真想归还的话,:源码中提供了“trimToSize()”,当然具体应用因人而异。
@Overridepublic T remove(int givePosition) {if ((givePosition >= 1) && (givePosition <= numberOfEntries)) {assert !isEmpty();T result = list[givePosition];if (givePosition < numberOfEntries) {removeGap(givePosition); // 移除后出现空隙,我们让之后的元素前移一位,弥补空隙 }return result;}return null;}/*** 给定位置为空隙,后续元素前进一位,补上空隙*/private void removeGap(int givePosition) {assert (givePosition >= 1) && (givePosition < numberOfEntries);int removedIndex = givePosition;int lastIndex = numberOfEntries;for (int index = givePosition; index < lastIndex; index++) {list[index] = list[index + 1];}}
tip: trimToSize的源码
/*** Trims the capacity of this <tt>ArrayList</tt> instance to be the* list's current size. An application can use this operation to minimize* the storage of an <tt>ArrayList</tt> instance.*/public void trimToSize() {modCount++;if (size < elementData.length) {elementData = (size == 0)? EMPTY_ELEMENTDATA // 这是个空数组 ---> {}: Arrays.copyOf(elementData, size); //吧数字copy进去,给一个size容量}}
toArray方法的实现
注意:千万不要直接返回ArrayList底层的数组,我们应该新申请一个空间,并将元素逐个放入其中。如果返回list,将成为一个非常恐怖的事情。
封装性:其他类只能通过我们定义好的方法去访问或改变我们对象中的域,不然就是封装遭到了破坏.
@Overridepublic T[] toArray() {/* 这里千万不能返回我们使用的T[] list* 因为用户可能会对返回数组进行修改,* 如果返回list,那么封装失败。* 也就是说用户可以不使用我们提供的方法进行操作List,这是很恐怖的事情。*/T[] result = (T[]) new Object[numberOfEntries];for (int index = 0; index < numberOfEntries; index++) {result[index] = list[index + 1];}return result;}
其余方法
@Overridepublic T replace(int givenPosition, T newEntry) {if ((givenPosition >= 1) && (givenPosition <= numberOfEntries)) {assert !isEmpty();T oldEntry = list[givenPosition];list[givenPosition] = newEntry;return oldEntry;}//省去报错的部分return null;}@Overridepublic T getEntry(int givenPosition) {// 判断给定位置是否合法,不合法就报错。if ((givenPosition >= 1) && (givenPosition <= numberOfEntries)) {assert !isEmpty();return list[givenPosition];}//else就报错,我省去此步return null;}@Overridepublic boolean contains(T anEntry) {boolean found = false;int index = 1;// 遍历寻找,找到一个就结束循环。while (!found && (index <= numberOfEntries)) {if (anEntry.equals(list[index])) {found = true;}index++;}return found;}
全部方法实现源码
package list;import java.util.Arrays;/*** 为了区分java默认的 ArrayList和我的list,所以命名为ArrayList*/ public class AList<T> implements ListInterface<T> {T[] list = null;int numberOfEntries = 0;static final int DEFAULT_CAPACITY = 27;@SuppressWarnings("unchecked")public AList(int initialCapacity) {T[] tempList = (T[]) new Object[initialCapacity];list = tempList;numberOfEntries = 0;}public AList() {this(DEFAULT_CAPACITY);}@Overridepublic void add(T newEntry) {// 与ArrayList 不同,我们选择下标从 1 开始。list[numberOfEntries + 1] = newEntry;numberOfEntries++;ensureCapacity();//也可以选择重用其他方法 // add(numberOfEntries+1,newEntry); // 这样重用,能有效减少代码,但是理解上稍微有些困难。并且增加了许多无谓的判断,降低了效率(当然这无所谓) }/*** 指定位置进行添加某一个数字** @param newPosition* @param newEntry*/@Overridepublic void add(Integer newPosition, T newEntry) {if ((newPosition >= 1) && (newPosition <= numberOfEntries + 1)) {if (newPosition <= numberOfEntries) {makeRoom(newPosition); // 将给定位置的数字及后续数字全部后移。 }list[newPosition] = newEntry;numberOfEntries++;ensureCapacity(); // 为下次添加获取足够的空间} else {throw new IndexOutOfBoundsException("给定位置不合法,下标越界");}}/*** @param newPosition*/private void makeRoom(Integer newPosition) {assert (newPosition >= 1) && (newPosition <= newPosition + 1); //断言,如果不满足断言,则报错,需要手动开启assertint newIndex = newPosition;int lastIndex = numberOfEntries;for (int index = 0; index < newIndex; index++) {list[index + 1] = list[index]; //给定位置之后的数字全部后移一位,将给定位置‘空’下来 }}@Overridepublic T remove(int givePosition) {if ((givePosition >= 1) && (givePosition <= numberOfEntries)) {assert !isEmpty();T result = list[givePosition];if (givePosition < numberOfEntries) {removeGap(givePosition); // 移除后出现空隙,我们让之后的元素前移一位,弥补空隙 }return result;}return null;}/*** 给定位置为空隙,进行弥补空隙,** @param givePosition*/private void removeGap(int givePosition) {assert (givePosition >= 1) && (givePosition < numberOfEntries);int removedIndex = givePosition;int lastIndex = numberOfEntries;for (int index = givePosition; index < lastIndex; index++) {list[index] = list[index + 1];}}@Overridepublic void clear() {list = null; //取消引用,等待GC回收numberOfEntries = 0;}@Overridepublic T replace(int givenPosition, T newEntry) {if ((givenPosition >= 1) && (givenPosition <= numberOfEntries)) {assert !isEmpty();T oldEntry = list[givenPosition];list[givenPosition] = newEntry;return oldEntry;}return null;}@Overridepublic T getEntry(int givenPosition) {if ((givenPosition >= 1) && (givenPosition <= numberOfEntries)) {assert !isEmpty();return list[givenPosition];}//else就报错,我省去此步return null;}@Overridepublic T[] toArray() {T[] result = (T[]) new Object[numberOfEntries];for (int index = 0; index < numberOfEntries; index++) {result[index] = list[index + 1];}return result;}@Overridepublic boolean contains(T anEntry) {boolean found = false;int index = 1;while (!found && (index <= numberOfEntries)) {if (anEntry.equals(list[index])) {found = true;}index++;}return found;}@Overridepublic int getLength() {return numberOfEntries;}@Overridepublic boolean isEmpty() {return numberOfEntries == 0;}/*** 倍扩容量*/private void ensureCapacity() {int capacity = list.length - 1;if (numberOfEntries >= capacity) {int newCapacity = 2 * capacity;list = Arrays.copyOf(list, newCapacity + 1);}} }
总结
如果说ArrayList 需要注意的地方
- 1.5倍扩容,思想很棒,但是比较浪费时间,所以程序员尽可能估算要使用的空间的大概上界,然后初始化时给定容量。
- toArray时,尽管底层有数组,但是千万不能返回底层的数组。
- clear时,直接将数组引用置为null,GC就可以回收到这块内存了。
1.5倍扩容:源代码:
int newCapacity = oldCapacity + (oldCapacity >> 1);//old + (old容量/ 2)
使用位移,更快。
LinkedList
实现接口
package list;/*** 为了区分LinkedList,所以命名为LList,但本质都是双向链表* 下标从 1 开始,与java源码不同(以0开始)*/ public class LList<T> implements ListInterface<T> {@Overridepublic void add(T newEntry) {}@Overridepublic void add(Integer newPosition, T newEntry) {}@Overridepublic T remove(int givePosition) {return null;}@Overridepublic void clear() {size = 0;first = null;}@Overridepublic T replace(int givenPosition, T newEntry) {return null;}@Overridepublic T getEntry(int givenPosition) {return null;}@Overridepublic T[] toArray() {return null;}@Overridepublic boolean contains(T anEntry) {return false;}@Overridepublic int getLength() {return 0;}@Overridepublic boolean isEmpty() {return false;}}
底层实现
明显使用链式,所以我们需要定义结点类Node。
其次,我们刚开始使用单向链表,利于实现与理解,之后会修改部分代码使其成了双向链表。并提供一些双向链表特有的操作。
public class LList<T> implements ListInterface<T> {int size;Node firstNode;// 链式实现无法给定容量,所以一个无参构造即可。public LList() {size = 0;firstNode = null;}//省去覆盖方法// 单向结点,只需要next和data就足够了class Node {T data;Node next;public Node() {}public Node(T data) {this(data, null);}public Node(T data, Node next) {this.data = data;this.next = next;}} }
核心方法:add的实现
@Overridepublic void add(T newEntry) {// 尾部位置进行添加 add(size, newEntry);}@Overridepublic void add(Integer newPosition, T newEntry) {Node newNode = new Node(newEntry);if (newPosition == 1) {//头结点插入newNode.next = firstNode;firstNode = newNode;} else if (newPosition > 1 && newPosition <= size) {Node beforeNode = firstNode;int count = 1;// 找到链表对应位置的前一位,进行插入操作while (count != newPosition - 1) {beforeNode = beforeNode.next;count++;}// 插入元素newNode.next = beforeNode.next; // 链接起来了beforeNode.next = newNode;} else {throw new IndexOutOfBoundsException("给定位置越界");}}
LinkedList全部源码
package list;/*** 为了区分LinkedList,所以命名为LList,但本质都是双向链表* 下标从 1 开始,与java源码不同(以0开始)*/ public class LList<T> implements ListInterface<T> {private int size;private Node firstNode;public LList() {size = 0;firstNode = null;}@Overridepublic void add(T newEntry) {// 尾部位置进行添加 add(size, newEntry);}@Overridepublic void add(Integer newPosition, T newEntry) {Node newNode = new Node(newEntry);if (newPosition == 1) {//头结点插入newNode.next = firstNode;firstNode = newNode;} else if (newPosition > 1 && newPosition <= size) {Node beforeNode = firstNode;int count = 1;// 找到链表对应位置的前一位,进行插入操作while (count != newPosition - 1) {beforeNode = beforeNode.next;count++;}// 插入元素newNode.next = beforeNode.next; // 链接起来了beforeNode.next = newNode;} else {throw new IndexOutOfBoundsException("给定位置越界");}}@Overridepublic T remove(int givePosition) {T result;if ((givePosition >= 1) && (givePosition <= size)) {assert !isEmpty();if (givePosition == 1) {// 头结点的移除result = firstNode.data;firstNode = firstNode.next;} else {Node nodeBefore = getNodeAt(givePosition - 1);Node nodeToRemove = nodeBefore.next;result = nodeBefore.data;Node nodeAfter = nodeToRemove.next;nodeBefore.next = nodeAfter; // 断开移除结点的两侧 }size--;return result;} elsethrow new IndexOutOfBoundsException("下标越界");}private Node getNodeAt(int i) {if (i > 0 && i <= size) {Node curNode = firstNode;for (int j = 0; j < i; j++)curNode = curNode.next;return curNode;} elsethrow new IndexOutOfBoundsException("下标越界");}@Overridepublic void clear() {size = 0;firstNode = null;}@Overridepublic T replace(int givenPosition, T newEntry) {// 判断和报错都交给 getNodeAt 方法去做Node nodeToReplace = getNodeAt(givenPosition);T result = nodeToReplace.data;nodeToReplace.data = newEntry;return result;}@Overridepublic T getEntry(int givenPosition) {return getNodeAt(givenPosition).data;}@Overridepublic T[] toArray() {T[] result = (T[]) new Object[size];Node curNode = firstNode;for (int i = 0; i < size; i++) {result[i] = curNode.data;}return null;}@Overridepublic boolean contains(T anEntry) {Node curNode = firstNode;boolean found = false;while (!found && curNode != null) {if (curNode.data.equals(anEntry)) {found = true;}curNode = curNode.next;}return found;}@Overridepublic int getLength() {return size;}@Overridepublic boolean isEmpty() {boolean result = false;// 为了提供更多的报错信息,在这里使用断言,需要手动开启断言功能。且程序开发中不建议使用。if (size == 0) {assert firstNode == null;result = true;} else {assert firstNode != null;result = false;}return result;}class Node {T data;Node next;public Node() {}public Node(T data) {this(data, null);}public Node(T data, Node next) {this.data = data;this.next = next;}} }
接下来只需要把单向链表改成双向链表即可,增加几个“getPrevNode(),addFirstNode()”之类的方法,修改add(),即可。
转载于:https://www.cnblogs.com/guodao/p/9702388.html
线性表ArrayList和LinkedList源码详解。相关推荐
- LinkedList源码详解
public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deq ...
- 【java】LinkedList1.8源码详解
目录 前言 概要 属性 构造方法 核心方法 get(int index) set(int index, E element) add(int index, E element) addAll(Coll ...
- java源码详解——String类
java源码详解--String类目录: Java String 类 下面开始介绍主要方法: Java charAt() 方法 Java compareTo() 方法 int compareTo(St ...
- Java集合框架源码详解系列(一)
写在前面:大家好!我是晴空๓.如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教.我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/.非常 ...
- 源码详解Android 9.0(P) 系统启动流程之SystemServer
源码详解Android 9.0(P) 系统启动流程目录: 源码详解Android 9.0(P)系统启动流程之init进程(第一阶段) 源码详解Android 9.0(P)系统启动流程之init进程(第 ...
- 【Live555】live555源码详解(二):BasicHashTable、DelayQueue、HandlerSet
[Live555]live555源码详解系列笔记 3.BasicHashTable 哈希表 协作图: 3.1 BasicHashTable BasicHashTable 继承自 HashTable 重 ...
- cocos android-1,Cocos2D-Android-1之源码详解:5.Box2dTest
Cocos2D-Android-1之源码详解:5.Box2dTest 发布时间:2020-08-06 06:19:28 来源:51CTO 阅读:398 作者:abab99 package org.co ...
- Redis从精通到入门——数据类型Zset实现源码详解
Redis数据类型之Zset详解 Zset简介 Zset常用操作 应用场景 Zset实现 源码阅读 Zset-ziplist实现 图解Zset-ziplist Zset-字典(dict) + 跳表(z ...
- java的数组与Arrays类源码详解
java的数组与Arrays类源码详解 java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用. 类的 ...
最新文章
- 导师:我不会拖延研究生正常毕业
- JavaScript 设计模式基础(二)
- C++系列总结——构造与析构
- 用Quartus II Timequest Timing Analyzer进行时序分析 :实例讲解
- Java 结构体之 JavaStruct 使用教程一 初识 JavaStruct
- deepin安装windows虚拟机_Deepin Linux V20系统通过安装wine实现运行windows程序
- 如何将网站升级为HTTPS协议(整理)
- leetcode955. Delete Columns to Make Sorted II
- Python 双y轴绘制
- Python练习-一辆购物车的寂寞都是Alex的错
- LuatOS之LVGL字体篇
- 失败的教训,总结下三个多月的考研历程(最终发现调剂非全复试成功了)
- Unity3D《打地鼠》学习笔记及心得
- 怎么往日历里面加时钟java,怎样在博客里添加钟表和日历
- 经典趣味数学问题之过河问题
- 李阳英语228句口语要素 +校园英语迷你惯用语 +1000句最常用英语口语
- HDOJ 2021-2030
- GYM 101128 H.Sheldon Numbers(枚举)
- Anaconda超详细安装教程(Windows环境下)
- Armadillo使用介绍(一):Armadillo介绍
热门文章
- [Delphi] Webbroker ISAPI 示例说明
- FineUI利用JS取控件的值
- 从指定文件夹里COPY指定的一批文件列表(TXT文件)
- Linq to XML 基本类
- android Adapter使用详解
- detime php_php试题及答案
- 在计算机技术中描述信息最小单位是,计算机二级考试单选题
- java float内存结构_Java后端开发岗必备技能:Java并发中的内存模型
- mysql约束条件整型_MySQL 数据类型(整型,浮点型,字符类型,日期类型,枚举和集合) 约束条件 自增...
- 中专是计算机专业毕业论文,中专计算机专业毕业论文内容