List接口、ArrayList類和LinkedList類
1.List
List接口繼承自Collection接口,其中常用的較爲重要的方法如下:
public interface List<AnyType> extends Collection<AnyType>{
int size();
boolean add(E e);
boolean remove(Object o);
E get(int index);
E set(int index, E element);
}
get和set使得用戶可以訪問或改變通過由位置索引index給定的表中指定位置上的項。索引0位於表的前端,索引size()-1代表表中的最後一項。
List有兩種流行的實現方式,ArrayList和LinkedList。
ArrayList、LinkedList
ArrayList類提供了List的一種可增長數組的實現.使用ArrayList的優點在於,對get和set的調用花費常數時間。其缺點是新項的插入和現有項的刪除代價昂貴,除非變動是在ArrayList的末端進行。
LinkedList則提供了List的雙向鏈表實現。使用LinkedList的優點在於,新項的插入和現有項的刪除均開銷很小,這意味着在表的前端進行添加和刪除都是常數時間的操作,由此LinkedList更提供了方法addFirst和removeFirst、addLast和removeLast等方法可以有效的添加、刪除和訪問表兩端的項。使用LInkedList的缺點是它不容易作索引,因此對get的調用是昂貴的,除非調用非常接近表的端點。
首先,我們在表的末端添加一項來構造list:
public static void makeList1(List<Integer> lst, int N) {
lst.clear();
for (int i = 0; i < N; i++) {
lst.add(i);
}
}
不管ArrayList還是LinkedListlist作爲參數進行傳遞,makeList1的運行時間都是o(N),因此對add的每次調用都是在表的末端進行從而花費常數時間。
如果我們從表的前端構造一個List:
public static void makeList2(List<Integer> lst, int N) {
lst.clear();
for (int i = 0; i < N; i++) {
lst.add(0, i);
}
}
那麼,對於LinkedList它的運行時間是O(N),但是對於ArrayList其運行時間則是O(N2),因爲在ArrayList中,在前端進行添加是一個O(N)操作。
下一個例子是極端List中的數的和:
public static int makeList3(List<Integer> lst, int N) {
int total = 0;
for (int i = 0; i < N; i++) {
total += lst.get(i);
}
return total;
}
這裏ArrayList的運行時間是O(N),但是對於LinkedList來說,其運行時間則是O(N2),因爲在LinkedList中,對get的調用爲O(N)操作。
如果使用一個增強的for循環,那麼它對任意List的運行時間都是O(N),因爲迭代器將有效地從一項到下一項推進。
將list表中所有偶數值刪除:
因爲ArrayList刪除效率地下,所以在涉及需要進行刪除操作時,我們應該構建LinkedList表。但是在LinkedList中,由於對get調用的效率不高,因此也會花費較多時間,所以remove的效率同樣低下,代碼如下:
public static void removeEvenVer1(List<Integer> lst){
int i = 0;
while (i<lst.size()){
if(lst.get(i)%2==0){
lst.remove(i);
}else {
i++;
}
}
}
如果我們不使用get()方法去獲取數據,而是使用迭代器,會不會變得高效一點呢
public static void removeEvenVer2(List<Integer> lst){
for (Integer x:lst){
if(x%2==0){
lst.remove(x);
}
}
}
但是我們運行這個程序,會產生一個異常,因爲當一項被刪除時,由增強的for循環所使用的基礎迭代器是非法的。
迭代器使用:
在迭代器找到一個偶數值項之後,我們可以使用該迭代器來刪除這個它剛看到的值。對於一個LinkedList,對該迭代器的remove方法的調用只花費常數時間,因爲該迭代器位於被刪除的節點。因此,對於LinkedList,整個程序花費線性時間,而不是二次時間。對於ArrayList,因爲數組的刪除伴隨着數組項的移動,所以花費的還是二次時間。
public static void removeEvensVer3(List<Integer> lst){
Iterator<Integer> itr = lst.iterator();
while (itr.hasNext()){
if(itr.next()%2 == 0){
itr.remove();
}
}
}
ArrayList類的實現
package collections.list;
import java.util.Iterator;
import java.util.function.Consumer;
/**
* Created with IntelliJ IDEA.
*
* @Author crazyang
* @Desciption:
* @Date 2018-6-8 17:04
*/
public class MyArrayList<AnyType> implements Iterator<AnyType> {
private static final int DEFAULT_CAPACITY = 10;
private int theSize;
private AnyType[] theItems;
public MyArrayList(){
doClear();
}
public void clear(){
doClear();
}
public void doClear(){
theSize = 0;
ensureCapacity(DEFAULT_CAPACITY);
}
public int size(){
return theSize;
}
public boolean isEmpty(){
return size() == 0;
}
public void trimToSize(){
ensureCapacity(size());
}
public AnyType get(int index){
if(index<0||index>=size()){
throw new ArrayIndexOutOfBoundsException();
}
return theItems[index];
}
public AnyType set(int index,AnyType newVal){
if(index<0||index>=size()){
throw new ArrayIndexOutOfBoundsException();
}
AnyType old = theItems[index];
theItems[index] = newVal;
return old;
}
public void ensureCapacity(int newCapacity){
if(newCapacity<theSize){
return;
}
AnyType [] old = theItems;
theItems=(AnyType[])new Object[newCapacity];
for(int i =0;i<size();i++){
theItems[i] = old[i];
}
}
public boolean add(AnyType x){
add(size(),x);
return true;
}
public void add(int index,AnyType x){
if(theItems.length == size()){
ensureCapacity(size()*2+1);
}
for(int i = theSize;i>index;i--){
theItems[i] = theItems[i-1];
}
theItems[index] = x;
theSize++;
}
public AnyType remove(int index){
AnyType removeItem = theItems[index];
for(int i = index;i<size()-1;i++){
theItems[i] = theItems[i+1];
}
theSize--;
return removeItem;
}
private int current=0;
@Override
public boolean hasNext() {
return current<size();
}
@Override
public AnyType next() {
if(!hasNext()){
throw new java.util.NoSuchElementException();
}
return theItems[current++];
}
@Override
public void remove() {
MyArrayList.this.remove(--current);
}
}
LinkedList類的實現
LinkedList將作爲雙向鏈表來實現,而且我們還需要保持該表兩端的引用。
設計:
1、MyLinkedList類本身,它包含到兩端的鏈、表的大小以及一些方法。
2、Node類,它可能是一個私有的嵌套類。一個節點包含數據以及到前一個節點的鏈和到下一個節點的鏈,還有一些適當的構造方法。
3、LinkedListIterator類,該類抽象了位置的概念,是一個私有類,並實現接口Iterator。它提供了next、hasNext和remove的實現。
package collections.list;
import java.util.Iterator;
/**
* Created with IntelliJ IDEA.
*
* @Author crazyang
* @Desciption:
* @Date 2018-6-8 17:29
*/
public class MyLinkedList<AnyType> implements Iterable<AnyType> {
private int theSize;
private int modCount = 0;
private Node<AnyType> beginMarker;
private Node<AnyType> endMarker;
private static class Node<AnyType> {
public AnyType data;
public Node<AnyType> prev;
public Node<AnyType> next;
public Node(AnyType d, Node<AnyType> p, Node<AnyType> n) {
data = d;
prev = p;
next = n;
}
}
public int size() {
return theSize;
}
public boolean add(AnyType x) {
add(size(), x);
return true;
}
public void add(int index, AnyType x) {
addBefore(getNode(index, 0, size()), x);
}
public void addBefore(Node<AnyType> p, AnyType x) {
Node newNode = new Node<>(x, p.prev, p);
newNode.prev.next = newNode;
p.prev = newNode;
theSize++;
modCount++;
}
public Node<AnyType> getNode(int index) {
return null;
}
public Node<AnyType> getNode(int index, int lower, int upper) {
return null;
}
@Override
public Iterator<AnyType> iterator() {
return null;
}
}