线性结构-实验报告

线性结构-实验报告

代码托管地址

实验-1

实验要求

查看ArrayList和LinkedList的Java API帮助文档,参考http://www.cnblogs.com/rocedu/p/4837092.html 用Junit对ArrayList和LinkedList的方法进行测试,要尽量覆盖正常情况,异常情况,边界情况

实验步骤

  • 参考积极主动敲代码,使用Junit学习Java程序设计
  • 查找ArrayList和LinkedList类的帮助文档,但因为方法太多且相近,按要求只测试了add、isEmpty、remove、contains三种,首先建立一个数组,放入两个元素。使用add方法,添加成功了返回true,失败了返回false; 测试是否为空,返回false;再调用remove方法删去这个元素

实验代码

 public class ArrayListTest  extends TestCase {


        public void testArrayList() throws Exception {
            ArrayList<String> list = new ArrayList<>();
            list.add("二系");
            list.add("三班");


            assertEquals(true, list.add("123"));//测试add(E e)方法
            list.add(3, "ABC");
            assertEquals("ABC", list.get(3));//测试get()和add(int index, E element)方法


            //测试isEmpty()方法
            assertEquals(false, list.isEmpty());

            //测试remove(int index)和remove(Object o)方法
            assertEquals("ABC", list.remove(3));
            assertEquals(true, list.remove("123"));
            assertEquals(false, list.remove("ABC"));
        }
    }

测试结果

实验-2

实验要求

分别用Java的ArrayList和LinkedList实现有序线性表的合并:
aList,bList都是非递减线性表,合并后也是非递减
public static List<? extends Comparable> mergeSortedList(List<? extends Comparable> aList,List<? extends Comparable> bList)

知识点

  • 将每轮比较过后偏小的那个节点从相应链表中删除(这是头节点的指针不会指向该节点了,但该节点的空间依旧保留),append(附加)到Lc链表。pa、pb始终指向当前La、Lb链表的第一个节点(头节点后面那个),最后会free掉La、Lb的头节点

  • 参考 有序线性表的有序合并

实验代码

public class MergeList {
public static LinkedList MergeList(LinkedList<? extends Comparable> aList,
                                  LinkedList<? extends Comparable> bList){
    LinkedList cList = new LinkedList();
    int i = 0;
    int j = 0;
    while (i<aList.size() && j <bList.size()) {
        if (aList.get(i).compareTo(bList.get(j)) < 0) {
            cList.add(aList.get(i));
            i++;
        } else {
            cList.add(bList.get(j));
            j++;
        }
    }
    if (i<aList.size())
        for (int a = i; a<aList.size()-1; a++)
            cList.add(aList.get(a));
    if (j<aList.size())
        for (int a = j; a<aList.size()-1; a++)
            cList.add(bList.get(a));
    return cList;
}

}

测试结果

实验-3

实验要求

参考Java Foundation 3rd 第15.6节,用数组实现线性表List
用JUnit或自己编写驱动类对自己实现的ArrayList进行测试

知识点

实验代码

public class TestArrayList extends TestCase {
ArrayList<String> List = new ArrayList<>();

public void testAdd() throws Exception {
    List.add("二系");
    List.add("三班");
    List.add("123");
    List.add("ABC");

    Assert.assertEquals("二系",List.get(0));
}


public void testRemove() throws Exception {
    List.add("二系");
    List.add("三班");
    List.add("123");
    List.add("ABC");

    Assert.assertEquals("123", List.remove(2));
}


public void testEmpty() throws Exception {
    List.add("二系");
    List.add("三班");
    List.add("123");
    List.add("ABC");

    Assert.assertEquals(false, List.isEmpty());
}

}

测试结果

实验-4

实验要求

参考Java Foundation 3rd 第15.7节,用链表实现线性表List
用JUnit或自己编写驱动类对自己实现的LinkedList进行测试

实验代码

public class TestLinkedList extends TestCase {
  LinkedList<String> List = new LinkedList<>();

public void testAdd() throws Exception {
    List.add("二系");
    List.add("三班");
    List.add("123");
    List.add("ABC");

    Assert.assertEquals("二系",List.get(0));
}


public void testRemove() throws Exception {
    List.add("二系");
    List.add("三班");
    List.add("123");
    List.add("ABC");

    Assert.assertEquals("123", List.remove(2));
}


public void testEmpty() throws Exception {
    List.add("二系");
    List.add("三班");
    List.add("123");
    List.add("ABC");

    Assert.assertEquals(false, List.isEmpty());
}

}

测试结果

实验-5

实验要求

参考http://www.cnblogs.com/rocedu/p/7483915.html对Java的ArrayList,LinkedList按要求进行源码分析

case

  • ArrayList提供了根据下标或者指定对象两种方式的删除功能。如下:

1、remove(int index):首先是检查范围,修改modCount,保留将要被移除的元素,将移除位置之后的元素向前挪动一个位置,将list末尾元素置空(null),返回被移除的元素。

public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

   return oldValue;
}

2、remove(Object o) : 移除此列表中首次出现的指定元素(如果存在)。即ArrayList中允许存放重复的元素

public boolean remove(Object o) {
   // 由于ArrayList中允许存放null,因此下面通过两种情况来分别处理。
   if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                // 类似remove(int index),移除列表中指定位置上的元素
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

首先通过代码可以看到,当移除成功后返回true,否则返回false。remove(Object o)中通过遍历element寻找是否存在传入对象,一旦找到就调用fastRemove移除对象。为什么找到了元素就知道了index,不通过remove(index)来移除元素呢?因为fastRemove跳过了判断边界的处理,因为找到元素就相当于确定了index不会超过边界,而且fastRemove并不返回被移除的元素。下面是fastRemove的代码,基本和remove(index)一致。

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work
}

3、

removeRange(int fromIndex,int toIndex):    
protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
                     numMoved);

    // clear to let GC do its work
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}
  • 使用ArrayList的注意事项:

1.ArrayList是基于数组的方式实现的,可以通过下标索引直接查找到指定位置的元素,因此查找效率高,但每次插入或删除元素,就要大量地移动元素,插入删除元素的效率低。
2.ArrayList在插入元素时,可能会进行数组的扩容,但是在删除元素时却不会减小数组的容量,如果希望减小数组的容量,可使用trimToSize方法,在查找元素要遍历数组时,对非null元素使用equals方法,对null元素使用==。
3.扩充容量的方法ensureCapacity。ArrayList在每次增加元素(可能是1个,也可能是一组)时,都要调用该方法来确保足够的容量。当 容量不足以容纳当前的元素个数时,就设置新的容量为旧的容量的1.5倍加1,如果设置后的新容量还不够,则直接新容量设置为传入的参数(也就是所需的容 量),而后用Arrays.copyof()方法将元素拷贝到新的数组。从中可以看出,当容量不够时,每次增加元素,都要将原来的元 素拷贝到一个新的数组中,非常之耗时,也因此建议在事先能确定元素数量的情况下,才使用ArrayList,否则建议使用LinkedList
4.ArrayList不是线程安全的。

接着我们看下LinkedList,LinkedList是基于双向链表的方式来实现的,双向链表就是集合中的每个元素都知道其前面的一个元素的位置和它后的一个元素位置。

在LinkedList中,使用一个内部类Entry来表示集合中的节点,元素的值赋值给element属性,节点的next属性指向下一个节点,节点的previous属性指向前一个节点。

private static class Entry<E> {
//element存放数据
E element;
//next属性指向当前节点的下一个节点
Entry<E> next;

  //previous属性指向当前节点的上一个节点
Entry previous;

Entry(E element, Entry<E> next, Entry<E> previous) {
    this.element = element;
    this.next = next;
    this.previous = previous;
}

}

/**
*维护了一个header节点,header节点的element属性为null,previouse属性为null,next属性为null,并且header节点是不存储数据的
*/
private transient Entry<E> header = new Entry<E>(null, null, null);
//size表示集合包含的元素个数
private transient int size = 0;
 /**
* 构造一个空的集合,将header节点的next属性、previous属性都指向header节点,这样形成了一个双向链表的闭环
*/
public LinkedList() {
     header.next = header.previous = header;
}

体会

posted @ 2017-09-26 22:51  20162325金立清  阅读(265)  评论(0编辑  收藏  举报