实验项目
- ArrayList和LinkedList测试
- 分别用Java的ArrayList和LinkedList实现有序线性表的合并
- 参考Java Foundation 3rd 第15.6节,用数组实现线性表List
- 参考Java Foundation 3rd 第15.7节,用链表实现线性表List
- 源码分析
代码链接
实验一
实验二
分别用Java的ArrayList和LinkedList实现有序线性表的合并:
aList,bList都是非递减线性表,合并后也是非递减
public static List<? extends Comparable>mergeSortedList(List<?extends Comparable> aList, List<? extends Comparable> bList)
测试mergeSortedList的正确性,要尽量覆盖正常情况,异常情况,边界情况.
- 源码
public class ArrayTest {
static List<MyArray> mArrayList1 = new ArrayList<>();
static List<MyArray> mArrayList2 = new ArrayList<>();
static List<MyArray> NewArrayList = new ArrayList<>();
public void Setup(){
setup();
}
public static void setup() {
mArrayList1.add(new MyArray(0));
mArrayList1.add(new MyArray(1));
mArrayList1.add(new MyArray(2));
mArrayList1.add(new MyArray(3));
mArrayList1.add(new MyArray(3));
mArrayList1.add(new MyArray(4));
mArrayList2.add(new MyArray(2));
mArrayList2.add(new MyArray(3));
mArrayList2.add(new MyArray(3));
mArrayList2.add(new MyArray(4));
mArrayList2.add(new MyArray(4));
mArrayList2.add(new MyArray(5));
}
public static List<? extends Comparable> mergeSortedList(List<? extends Comparable> aList,
List<? extends Comparable> bList){
setup();
List<Comparable> mlist1 = new ArrayList();
for (int i =0;i<aList.size();i++){
mlist1.add(aList.get(i));
}
for (int i =0;i<bList.size();i++){
mlist1.add(bList.get(i));
}
selectionSort(mlist1);
return mlist1;
}
public static void selectionSort(List<Comparable> myList){
int min;
for (int index =0;index<myList.size()-1;index++){
min = index;
for (int scan = index+1;scan < myList.size();scan++){
if (myList.get(scan).compareTo(myList.get(min))<0)
min = scan;
}
swap(myList,min,index);
}
}
private static void swap(List<Comparable> myList,int index1,int index2){
List<Comparable> list = new ArrayList();
list.add(myList.get(index1));
myList.set(index1,myList.get(index2));
myList.set(index2,list.get(0));
}
}
- 我的思路是:两个线性表都是有序的,所以我定义一个线性表,现将原有表的元素都加入表中,再将表中的元素进行排序,调用selectionSort方法和swap方法,将元素进行比较排列。
实验三
用JUnit或自己编写驱动类对自己实现的ArrayList进行测试。
- 源码
public class MyList<T> {
private static int FirstRoom = 20;
private T [] num=(T [])new Object [FirstRoom];
private int thesize;
public static void setFirstRoom(int firstRoom) {
FirstRoom = firstRoom;
}
public int getThesize() {
return thesize;
}
public static int getFirstRoom() {
return FirstRoom;
}
public void setlist(){
thesize=0;
}
//ArrayList中的add(E e):将指定的元素添加到此列表的尾部。
public boolean add(T t){
add(thesize,t);
return true;
}
public void add(int index,T t){
if (index<0){
System.out.println("Wrong number");
}
if (index>thesize){
//ruguo
}
if (index == thesize){
num[index] = t;
}
else if(index<thesize){
for (int i = thesize;i>index;i--){
num[i] =num [i-1];
}
num[index] = t;
}
thesize++;
}
//删除表中的元素 ArrayList remove(Object o)
public boolean remove(T t){
int position = -1;
for (int i=0;i<thesize;i++){
if (num[i]==t){
position = i;
break;
}
}
if (position==-1){
System.out.println("Wrong position");
return false;
}
else {
for (int i= position;i<thesize-1;i++){
num[i] = num[i+1];
}
thesize --;
return true;
}
}
public T get(int index){
T item = num[index];
return item;
}
public boolean isEmpty(){
if (thesize==0){
return true;
}
else return false;
}
}
- 我的思路:首先通过强制转型定义数组
private T [] num=(T [])new Object [FirstRoom];
,private int thesize;
是为了反馈数组里的元素个数。 - 添加元素:将元素添加到指定位置,那么原位置的元素向后移。
- 删除元素:删除指定位置的元素,那么原位置元素的后一位向前移。
实验四
-
用JUnit或自己编写驱动类对自己实现的LinkedList进行测试。
-
源码
public class Linked<T> {
private int size;
private Point FirstPoint;//表的头
private Point LastPoint;//表的尾
public Linked(){
FirstPoint = new Point(null,null,null);//开始表头为空
LastPoint = new Point(null,FirstPoint,null);
FirstPoint.EndPoint=LastPoint;
}
class Point{
T data;
Point HeadPoint;
Point EndPoint;
Point(T data,Point headPoint,Point endPoint){
this.data = data;
this.HeadPoint = headPoint;
this.EndPoint = endPoint;
}
};
public int size(){
return this.size;
}
public boolean isEmpty(){
return size()==0;
}
public boolean add(T t){
Point point = new Point(t,LastPoint.HeadPoint,LastPoint);
LastPoint.HeadPoint.EndPoint = point;
LastPoint.HeadPoint = point;
size++;
return true;
}
public void remove(int index) {
Point point = getposition(index);
point.HeadPoint.EndPoint = point.EndPoint;
point.EndPoint.HeadPoint = point.HeadPoint;
point.data = null;
point.HeadPoint = null;
point.EndPoint = null;
point = null;
size--;
}
public T get(int index){
Point point = getposition(index);
return point.data;
}
private Point getposition(int index){
Point point;
int i;
for (i =0,point=FirstPoint.EndPoint;i<index;i++){
point = point.EndPoint;
}
return point;
}
}
- 我的思路:我设计的是一个双向链表,一个节点有两个指针,一个指向此节点的前节点,一个指向后一个节点。首先要定义列表中的头尾两个节点,此节点都不存储任何数据。
firstNode = new Node(null,null,null);
lastNode = new Node(null,firstNode,null);
firstNode.nextNode = lastNode;
初始化头节点,此时头节点的前驱节点指向null,后继节点指向null
因为此时尾节点还没初始化,无法指向
初始化尾节点,此时尾节点的前驱节点指向头节点,后继节点指向null
将头节点的后继节点指向尾节点
- 添加节点:我的添加方式是按顺序添加,首先新建一个节点,前驱节点指向尾节点的前一个节点,后继节点指向尾节点,原先尾节点的前一个节点的后继节点指向我们新建的节点,尾节点的前驱节点指向我们新建的节点。
public boolean add(T t){
Point point = new Point(t,LastPoint.HeadPoint,LastPoint);
LastPoint.HeadPoint.EndPoint = point;
LastPoint.HeadPoint = point;
size++;
return true;
}
- 删除节点:我的删除方法是删除指定位置的元素,首先要找到位于该位置的节点,将找到节点的前驱节点的后继结点指向该节点的后继节点,将找到节点的后继结点的前驱节点指向该节点的前驱节点,将该节点的数据域、指针域以及它本身都指向null。
public void remove(int index) {
Point point = getposition(index);
point.HeadPoint.EndPoint = point.EndPoint;
point.EndPoint.HeadPoint = point.HeadPoint;
point.data = null;
point.HeadPoint = null;
point.EndPoint = null;
point = null;
size--;
}
private Point getposition(int index){
Point point;
int i;
for (i =0,point=FirstPoint.EndPoint;i<index;i++){
point = point.EndPoint;
}
return point;
}
实验五:ArrayList和LinkedList的源码分析
- 1.构造函数之一:ArrayList(int initialCapacity)
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
该方法的作用是:构造一个具有指定初始容量的空列表。传入参数可能有三种情况:(1)init>0,此时
this.elementData = new Object[initialCapacity];
;(2)如果init<0,则抛出异常;
- 2.函数方法:contains(Object o)
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
contains方法调用了indexOf()方法,而indexOf方法的源码注释:返回列表中第一次出现元素的索引,或者返回-1,如果列表中不存在此元素。indexOf方法处理了三种情况:(1)o ==null,定义一个循环遍历此表,如果出现元素为空的情况则返回此元素的位置。(2)如果o不为空,同样定义 循环,如果
o.equals(elementData[i])
则返回此元素位置。(3)如果没有次元素则返回-1;而contains方法中将返回的数与0进行比较。
- 函数方法:add(int index, E element)
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 对比自己写的add()方法,感慨自己确实年轻太多了,考虑的太少了。add()方法,在正式行使它的作用之前做了几件十分重要的事情,(1)调用 rangeCheckForAdd()方法判断参数index即要插入元素的位置是否合乎逻辑;(2)调用ensureCapacityInternal()方法来改变原来数组的容量。
改变数组容量的具体过程:在ensureCapacityInternal()方法中先判断数组elementData是否为空,进而将参数minCapacity赋值为默认容量和mimCapacity中的最大值。继而调用 ensureExplicitCapacity()方法,传入的参数依旧为minCapacity,该方法依旧有一个if语句:如果minCapacity>原数组的长度,则调用最后的grow()方法,对此方法传入的参数依旧是minCapacity;该方法中有两个Int类型数值:oldCapacity和newCapacity,顾名思义一个为数组之前的容量,等于数组的长度,另一个是新容量为原容量的1.5倍。两个if语句是用来判断newCapacity是否合适,最后使用Arrays类的copyOf方法来重新定义数组。该方法的作用是:复制指定的数组,截取或用 0 填充(如有必要),以使副本具有指定的长度。对于在原数组和副本中都有效的所有索引,这两个数组将包含相同的值。对于在副本中有效而在原数组无效的所有索引,副本将包含 (byte)0。当且仅当指定长度大于原数组的长度时,这些索引存在。
-
(3)直接调用System.arraycopy方法,该方法的作用是: ,就好像假定元素组元素有:1,2,3,4,5。他们的编号为0,1,2,3,4。若我们插入6到第二个位置,即为1,2,6,3,4,5。那么经过一次调用System.arraycopy方法后数组暂时变为了:1,2,3,3,4,5。最后只需要将数组的第二个位置的元素赋值为我们要插入的元素即可,本例:
elementData[index] = element;
-
而我的方法则是: 而写的函数考虑问题不够全面,添加元素的方法不够方便,较为笨重。