天天看點

List集合基礎複習

                               List集合接口主要使用類

      本章主要複習實作list接口的幾個類,側重于說明簡單的使用方法和基本注意事項以及各個的使用優缺點。

List代表一個元素有序,可以重複的集合,集合中的每個元素都有其對應的順序索引。

List集合是Collection集合的子接口,是以有Collection的很多方法,而List又新增加了很多根據索引操作元素的集合。

Void  add(int I ,Object  obj);

BooleanaddAll(int index,Collection c);

IntindexOf(Object o);

Object   remove(int index);

Int lastIndexOf(intindex);

Object set(intindex,Object obj);

List  subList(int fromIndex, int toIndex);

與set集合相比List增加了根據索引來插入,替換和删除集合元素的方法。

首先是ArrayList的示範。

執行個體代碼一:

package cn.com.list;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
 * 
 * @author fcs
 * List接口的實作類練習測試:ArrayList進行測試,注意另外一個實作類Vector是線程安全的,而ArrayList不是線程安全的
 */
public class ArrayListDemo {
   public static void main(String[] args) {
	List<String>  books = new ArrayList<String>();  //java1.6及以下可以使用這種方式
	List          foods = new ArrayList();          //由于List接口支援泛型機制是以,在這種情況下會進行泛型檢測
	
	/*添加:有四種方式*/
	books.add(new String("瘋狂java講義"));
	books.add(1, "Effective java中文版"); 
	Collection<String> booksColl = new ArrayList<String>();
	booksColl.add("Linux基礎");
	booksColl.add("計算機網絡");
	books.addAll(2, booksColl);  //添加集合
	books.addAll(booksColl);
	books.add("瘋狂java講義");
	/*查找和周遊*/
	boolean b = books.contains("瘋狂java講義");
	System.out.println("b = "+b);
	boolean b1  = books.containsAll(booksColl); //判斷是否包含集合
	System.out.println("b1 = "+b1);
	String listStr = books.get(2);       //說明:雖然我們在上面的第三個位置即索引為2的地方加入到額是集合,
										 //但是List會往後一個一個的添加,是以再次得到的第二個索引的值是該集合的首元素
	System.out.println(listStr);
	int   index1 = books.indexOf("瘋狂java講義");   //傳回該參數中對象的第一次出現的位置,如果沒有
	System.out.println("index1 = "+index1);
	index1 = books.indexOf(2);    
	System.out.println("index1 = "+index1);       //當沒有出現的時候則傳回-1.
	index1 = books.lastIndexOf("瘋狂java講義");
	System.out.println("index1 = "+index1);       //傳回指定元素最後一次出現的位置
	
	ListIterator<String> listIt = books.listIterator();   //正向周遊
	while(listIt.hasNext()){                              // listIt.hasPrevious():實作反向周遊
		System.out.print(listIt.next()+"--");    
	}
	System.out.println();
	
	Iterator<String> listIt1 = books.iterator();         //使用Iterator接口的方法隻能實作正向周遊
	while(listIt1.hasNext()){ 
		System.out.print(listIt1.next()+"--");            
	}
	System.out.println();
	for(int i =0;i<books.size();i++){             //使用for循環周遊,不過這種方式比較低級,在資料量比較大的時候性能不如增強for循環
		System.out.print(books.get(i)+"--");
	}
	System.out.println();
	for(String bookStr : books){
		System.out.print(bookStr+"--");
	}
	System.out.println();
	listIt = books.listIterator(1);        //從指定索引處開始周遊
	while(listIt.hasNext()){
		System.out.println(listIt.next());
	}

	/*修改*/
	books.set(2, "作業系統");    
	for(String bookStr : books){
		System.out.print(bookStr+"--");
	}
	books.isEmpty();                //判斷集合是否為空

	/*删除:*/
	//books.clear();                  //清空集合,但是該并沒有為null,隻是裡面沒有元素而已
	System.out.println(books.size());     //擷取該集合的大小
	System.out.println(books == null ? "true":"false");
	//books.remove(2);              //通過索引删除指定元素
	//books.remove("瘋狂java講義");   //直接删除指定元素
	//books.removeAll(booksColl);   //删除放入的集合
	//b = books.containsAll(booksColl);
	//System.out.println("b = "+b);
	
	/*轉換*/
	String booksList = books.toString();   //轉換成String
	System.out.println(booksList);
	List<String> newbooks = books.subList(0, 3);                   //截取子集合
	System.out.println(newbooks.size());
	//String [] booksArray = (String[]) books.toArray();             //這種方式是錯誤的,在進行強制轉換的時候會報錯
	Object[]  booksArray = books.toArray();
	System.out.println(booksArray.length);
  }
}
           

上面的方法中有一個接口說明一下:

這個是針對list集合周遊提出的。

List可以通過繼承Collection得到的Iterator接口的方法進行周遊,也可以通過listIterator方法進行周遊,傳回一個ListIterator對象,而這個ListIterator則繼承了Iterator接口,提供了專門操作List的集合。

ListIterator接口在Iterator接口上又增加了下面的幾個方法:

1.booleanhasPrevious();       傳回該疊代器關聯的集合對象是否還有上一個元素,可以進行逆序周遊。

2.Object  previous();       傳回該疊代器的上一個元素

3 .void  add(E e);          将指定的元素插入清單中。

而在JDK 1.6的API中對該接口有如下定義:

系清單疊代器,允許程式員按任一方向周遊清單、疊代期間修改清單,并獲得疊代器在清單中的目前位置。ListIterator 沒有目前元素;它的光标位置 始終位于調用previous() 所傳回的元素和調用 next() 所傳回的元素之間。長度為 n 的清單的疊代器有n+1 個可能的指針位置。

remove()

set(Object)

方法不是 根據光标位置定義的;它們是根據對調用

next()

previous()

所傳回的最後一個元素的操作定義的

然後是list判斷集合元素(對象)相等的測試以及一個文章引發的測試。

執行個體代碼二:

/**
 * List如何判斷兩個元素對象是否相等
 * 說明:list判斷兩個對象元素是否相等是通過equals方法進行比較的,而且在比較時采用的方法是覆寫Object類的那個方法。
 * @author fcs
 *
 */
class A{
	//注意這裡内涵玄機:比較的時候參數要是Object類型的,否則如果直接拿對象進行比較是無法判斷新對象元素與已經存在的對象元素
	//相等的,原因是該方法判斷元素相等是通過Object超類的equals方法判斷的,如果參數類型不是Object類型則不能覆寫,也就是說是方法重載的問題。
	//這也是CSDN上面博友讨論的一個問題,在這裡說一下,下面給出一個例子
	public boolean equals(Object obj){
		return true;
	}
}
public class ListEquals {
    public static void main(String[] args) {
    	List<String>  booksList = new ArrayList<String>();
    	booksList.add(new String("A"));
    	booksList.add(new String("B"));
    	booksList.add("C");
    	booksList.add("D");
    	System.out.println(booksList.toString());
    	booksList.remove(new A());                 //這裡每次比較都會通過equals方法,設定預設每次都為true,是以每次比較都會删除第一個元素,而
    											   //而remove的方法參數為Object類型。
    	System.out.println(booksList.toString());
    	booksList.remove(new A());
    	System.out.println(booksList.toString());
    	/*----------下面我們來看CSDN上面的一個文章的問題:List判斷兩個對象是通過equals方法進行的?----------*/
        Product p1 = new Product(1,"A");
        Product p2 = new Product(2,"B");
        Product p3 = new Product(3,"C");
        
        Product p2_1 = new Product(2, "X");   //當然這裡隻是以id為判斷标準,也可以以兩個都為判斷标準
        List<Product> proList = new ArrayList<Product>();
        proList.add(p1);
        proList.add(p2);
        proList.add(p3);
        
//        //測試一:以第一個equals方法為準。
//        for(Product pro : proList){
//        	if(p2_1.equals(pro)){
//        		System.out.println("true"); 
//        	}
//        }
//        System.out.println(proList.indexOf(p2_1));   //這裡傳回的是-1,也就是說沒有id=2的元素,問題就在這裡。
//        
//        
//        //測試二:以第二個equals方法為準。
//        for(Product pro : proList){
//        	if(p2_1.equals(pro)){
//        		System.out.println("true");     
//        	}
//        }
//        System.out.println(proList.indexOf(p2_1));   //這裡會傳回1,表明第二個equals方法可以達到通過id判斷是否相等的要求。
//    
    
        //測試三:以第三個equals方法為準,編輯器以兩個屬性作為判斷标準,将p2_1的name值再改為B進行測試
        for(Product pro : proList){
        	if(p2_1.equals(pro)){
        		System.out.println("true");       
        	}
        }
        System.out.println(proList.indexOf(p2_1));   //p2_1.name = B傳回1,當p2_1.name = X,傳回-1
        //注意這種比較方式與hashcode方法沒有關系,下面沒有重寫該方法同樣可以實作測試要求和目的。
    
    }
}
class Product{
	private int id;
	private String name;
	
	public Product(int id, String name) {
		this.id = id;
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
//	//第一種equals方法,這種方式算與下面的equals方法是重載的關系
//	public boolean equals(Product p){
//		if(this.id == p.getId()){
//			return true;
//		}
//		return false;
//	}
	
//	@Override             //注意在重寫方法的時候加上這個注解就會少很多錯。
//	public boolean equals(Object p){
//		//當然這裡的對象要進行一次強制轉換
//		Product pro = (Product)p;
//		if(this.id == pro.getId()){
//			return true;
//		}
//		return false;
//	}
	//另外我們通過eclipse的編輯器自動生成的比較方法也可以看出是這種結果,這是一種比較标準的寫法
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Product other = (Product) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}
           

在CSDN上的這個文章的一個博友回答可以使用實作Comparable接口的方式進行判斷,是以我也針對這個方法進行了與樓主同樣的測試,測試結果說明都在代碼中的注釋裡,這裡就不贅述了。

執行個體代碼三:

/**
 * List中比較兩個對象想的方式:通過Comparable接口進行比較兩個對象的方式,
 * 當然Comparable接口着重于進行排序,
 * @author fcs
 *
 */
@SuppressWarnings("rawtypes")
class Foods implements Comparable{
	private int id;
	private String name;
	public Foods(int id, String name) {
		this.id = id;
		this.name = name;
	}
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int compareTo(Object o) {    //這裡要實作的方法同樣要進行類型轉換
		Foods  foods = (Foods)o;
		return foods.getId() - this.getId();    
	}
	
}
public class ListEquals_2 {
	public static void main(String[] args) {
		  List<Foods>   strList = new ArrayList<Foods>();
		  strList.add(new Foods(1,"蔬菜"));
		  strList.add(new Foods(2,"水果"));
		  strList.add(new Foods(3,"肉類"));
		  Foods  foods = new Foods(2,"水果");
		  for(Foods strFoods : strList){
			  if(strFoods.compareTo(foods) == 0){
				  System.out.println("true");                 //會輸出為true,可以達到通過對象的id進行相等的測試,
				  											  //不過這裡實際上采用了比較低級的方式:是通過周遊進行的測試。
			  }
		  } 
		  int b = strList.indexOf(foods);                     //但是這裡輸出的是-1,說明不是同一個對象。是以使用Comparable接口進行比較實際上不能滿足性能上的要求
		  System.out.println("b = "+b);      
	}
}
           

第二 實作List的Stack和Vector說明:

ArrayList和Vector都是作為List類的兩個基本實作,而且是基于數組的實作方式。其中有一個屬性是initialCapacity參數,通過該參數可以設定該數組的長度。在一次性添加大量的元素時,可以使用ensureCapacity(int minCapacity)方法一次性增加initialCapacity的值,可以減少重配置設定的次數,提高性能。

   如果知道需要多少空間存儲元素,則可以在建立的時候就指明initialCapacity的大小、

   除此之外:下面了的兩個方法可以幫助重新配置設定Object[]數組的空間:

Void  ensureCapacity(int mincapacity)将ArrayList或者、Vector集合的Object[]數組長度增加minCapacity;

Void  trimToSize():  調整ArrayList或者Vector集合元素的object[]數組的長度為目前元素的個數,這樣可以減少ArrayList和Vector對象占用的空間。

第三: 固定長度的List:

執行個體代碼,四import java.util.Arrays;
import java.util.List;

/**
 * 固定長度的list
 * Arrays.ArrayList是一個固定長度的list集合
 * @author fcs
 *
 */
public class ArrayList {
    public static void main(String[] args) {
	     List   fixedList =  Arrays.asList("javaman","jsgirl");
	     System.out.println(fixedList.getClass());      //注意這裡輸出的是.Arrays$ArrayList是Arrays的内部類arrayList的執行個體。
	     for(int i =0;i<fixedList.size();i++){
	    	System.out.println(fixedList.get(i)); 
	     }
	     //試圖增加,删除元素都将引發UnsupportedOperationException
	     fixedList.add("zhangsan");
	     fixedList.remove(2);
    }
}
           

這裡引用了前面的Arrays操作數組的工具類,裡面的asList(object … a)方法,該方法可以把一個數組或指定個數的對象轉換成一個List集合,這個集合比較特殊:

既不是ArrayList類的實作類,也不是Vector的實作類,而是Arrays的内部類ArrayList的執行個體:

Arrays.ArraList是一個固定長度的List的集合,程式隻能周遊該集合裡的元素,不能增加,删除該集合裡的元素。

執行個體代碼五:

import java.util.Arrays;
import java.util.List;

/**
 * 固定長度的list
 * Arrays.ArrayList是一個固定長度的list集合
 * @author fcs
 *
 */
public class ArrayList {
    public static void main(String[] args) {
	     List   fixedList =  Arrays.asList("javaman","jsgirl");
	     System.out.println(fixedList.getClass());      //注意這裡輸出的是.Arrays$ArrayList是Arrays的内部類arrayList的執行個體。
	     for(int i =0;i<fixedList.size();i++){
	    	System.out.println(fixedList.get(i)); 
	     }
	     //試圖增加,删除元素都将引發UnsupportedOperationException
	     fixedList.add("zhangsan");
	     fixedList.remove(2);
    }
}
           

第四:List接口的LinkedList的實作類:

該實作類不僅實作了List而且實作了Deque接口,是以可以被當成雙端隊列實作,也可以當成特殊的棧使用。

下面是簡單的執行個體:

 執行個體代碼六:

import java.util.LinkedList;
/**
 * LinkedList的簡單介紹:
 * linkedList實作了List,Deque,Queue,List接口,是以可以當做連結清單集合,可以實作隊列,雙端隊列和棧的功能
 * 
 * @author fcs
 *
 */
public class LinkedListDemo {
   public static void main(String[] args) {
	   LinkedList<String> linkedList = new LinkedList<String>();
	   linkedList.add("A");
	   linkedList.add("B");
	   linkedList.offer("X");                  //将字元元素加入隊列的尾部

	   linkedList.add("C");
	   for(String strlist : linkedList){
		   System.out.println(strlist);
	   }
	   System.out.println(linkedList.peekFirst());     //通路但不删除棧頂元素
	   System.out.println(linkedList.peekLast());      //通路但不删除隊列的最後一個元素
	   System.out.println(linkedList);
	   
	   System.out.println(linkedList.pop());            //将棧頂的元素彈出棧,也就是删除棧頂元素
	   System.out.println(linkedList);
	   
	   System.out.println(linkedList.pollLast());       //将隊列中的最後一個元素删除
	   System.out.println(linkedList);
   }
}
           

上面的一些其他方法可以在JDK1.6的中文文檔中查到,這裡就不進行示範了。

本章的一部分代碼和原理參考了李剛的瘋狂java講義,同時本系列部落格也是全面掃盲的基礎和實踐,歡迎拍磚交流。