1. 程式人生 > >重新開始學Java——集合框架之Collection

重新開始學Java——集合框架之Collection

為什麼需要集合框架呢?因為之前使用陣列進行儲存物件的時候,會發現,經常不能夠確定大小的存在,那麼這裡就需要使用集合框架的存在。集合框架分為兩種,第一個是 java.util.Collection ;而另外一個是java.util.Map。這是兩種不同的東西,所以,本部落格先來描述 Collection 的使用,以及其實現方式。

java.util.Colllection

宣告

public interface Collection<E> extends Iterable<E>

可以看到這裡是一個介面,繼承了Iterable介面。先不管<>內部的東西是什麼,也先不管Iterable這個介面是幹什麼的。

描述

官方描述如下:

The root interface in the collection hierarchy. A collection represents a group of objects, known as its elements. Some collections allow duplicate elements and others do not. Some are ordered and others unordered. The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List. This interface is typically used to pass collections around and manipulate them where maximum generality is desired.

翻譯如下: 集合層次結構中的根介面。集合表示一組物件,稱為其元素。某些集合允許重複元素,而其他集合不允許。有些是有序的,有些是無序的。JDK 不提供此介面的任何直接實現:它提供更具體的子介面(如 Set 和 List)的實現。此介面通常用於傳遞集合,並在需要最大通用性時操作它們。

那麼就可以知道:java.util.Collection是集合層次的根介面,其中的物件稱為元素,元素可能有序,也可能無序,不建議直接實現java.util.Collection介面,有更加具體的子介面(Set、List)。最常的使用的方式是用來傳遞集合(比如將Set轉成List、將List轉成Set等操作)。

Collection的使用

注意:Collection是一個介面,那麼在使用的過程中,要麼使用匿名內部類的方式實現,要麼使用其子類進行例項化操作。

可以看到具體的實現子類是比較多的,那麼在這裡就使用HashSet作為實現子類,從而瞭解一下Collection的使用以及其內部方法。

public class TestCollection1 { // 用 HashSet 測試 Collection 中的方法
    public static void main(String[] args) {
        Collection<String> coll = new HashSet<>();// 無序的
        coll.add("孫悟空");
        coll.add("白龍馬");
        coll.add("豬悟能");
        coll.add("沙悟淨");
        System.out.println(coll);// coll.toString();
        System.out.println(coll.size());
        System.out.println(coll.contains("猴子"));
        System.out.println(coll.contains("孫悟空"));
        String[] array = new String[5];
        String[] returnArray = coll.toArray(array);
        System.out.println(array == returnArray);
        for (int i = 0; i < returnArray.length; i++) {
            System.out.println(array[i]);
        }
        Collection<String> xiyouTeam = new HashSet<>();
        xiyouTeam.add("陳玄奘"); //
        xiyouTeam.addAll(coll); // 將 coll 中的內容全部新增到xiyouTeam 集合中
        System.out.println(xiyouTeam);
        System.out.println(coll);
        // 判斷 coll 中包含的元素是否在 xiyouTeam 中全部都存在
        // 判斷 xiyouTeam 集合中是否包含 coll 中的每一個元素
        System.out.println(xiyouTeam.containsAll(coll));
        // 從 xiyouTeam 集合中刪除 coll 所包含的所有元素
        xiyouTeam.removeAll(coll);
        System.out.println(xiyouTeam);
        // 將 coll 中的內容全部新增到 xiyouTeam 集合中
        xiyouTeam.addAll(coll);
        System.out.println(xiyouTeam);
        // 保留 xiyouTeam 集合中的 coll 包含的那些元素,刪除其 元素,只要是 coll 中存放的元素留下來。
        xiyouTeam.retainAll(coll);
        System.out.println(xiyouTeam);
    }
}

那麼在這裡,可以發現大部分的方法都進行了舉例應用,具體的方法描述可以進行檢視官方API。

泛型

泛型是Java SE 1.5的新特性,泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。 Java語言引入泛型的好處是安全簡單。————百度百科

首先可以建立一個MySet,在實現新增方法,但是這個方法只能新增 Object 型別的物件。

public class MySet {
	public void add( Object o) {
		System.out.println("新增 : "+ o );
	}
}

然後寫一個測試類,如下所示:

public class TestMySet{
	public static void main(String[] args) {
		MySet ms = new MySet();
		ms.add(250);
		ms.add("hello");
	}
}

通過上述的例子可以發現這樣的缺點:型別比較雜亂,所以我們需要新增一個限制。

然後建立一個MyList 的類:

/**
*為該類指定型別引數
* @param <E> 形參引數 E 用來確定型別,叫型別引數
* 可以在宣告 類 或介面時,使用泛型引數
*/
public class MyList<E>{
	public void add(E o){
		System.out.println("儲存: " + o);
	}
}

根據上邊的MyList類進行測試:

public class TestMyList {
	public static void main(String[] args) {
		// 使用時可以通過<> 為型別引數傳入實參
		MyList<String> list = new MyList<>(); // 有黃線表示 不確定型別
		// 因為宣告時 使用了泛型引數(實參),因此 add 方法的	傳入引數要求必須跟泛型引數一致
		list.add("hello");
		//list.add(250); // 當引數與泛型引數不一致時,編譯無法通過(失敗)
		/**JDK 1.7 開始支援“菱形語法”: 等號之後可以不用寫泛型引數,
		* 通過“感應”等號前宣告的變數型別的泛型引數來確定
		*/
		MyList<Integer> numbers = new MyList<>();
	}
}
泛型方法

首先建立一個實體類:

public class Tank {
	private Integer id;
	private String name;
	// getter and setter
}

然後建立對應的測試類:

/**
* 可以在方法宣告中,使用泛型引數
* 如果引數中使用了泛型,可以通過入參來確定泛型的具體型別
* 如果引數中,沒有使用泛型,則可以通過宣告“接收者”來確定返
回值的型別29
*/
public class Flighter {
	public static void main(String[] args) {
		Flighter f = new Flighter();
		String o = new String("hello");
		// 通過傳入的實際引數確定了返回值的型別
		String s = f.attack(o);//這個地方如何確定傳入的型別
		System.out.println(s);
		// 通過等號前宣告的那個變數的型別確定被呼叫的泛型方法返回值的型別
		Tank t = f.create(999, "蘇-890");// 不寫 Tank 的時候,返回 Object ,寫了 Tank 之後,返回 Tank
		System.out.println(t.getName());
	}
	/**
	* 定義一個泛型方法(在返回型別和引數中使用泛型)
	* @param o 需要攻擊的物件
	* @return 返回被攻擊的物件
	<E> 表示傳入到這個方法中的引數型別
	*/
	public <E> E attack(E o){
		System.out.println("向" + o +"發起進攻");
		return o;
	}
	/**
	* 定義一個泛型方法(在返回型別使用泛型、引數中沒有使用)
	* 指定了泛型引數的上限,將來實際傳入的泛型引數必須是某
	個型別的後代(可以是他自己)
	*/
	// 參看 LinkedList 中的 addAll 中的(Collection< ? extendsE>)就是說你可以是任意型別,但是必須繼承 E
	public <T extends Tank> T create(int id, String name){// 在這裡寫一個泛型,但是最好是 Tank 的後代
		// 可能是 Tank 向子類型別轉換(可能觸發型別轉換異常)
		T tank = ( T )new Tank(); // 這裡要想 :多型:父類型別	引用指向子類型別物件,但是子類型別引用不能指向父類型別物件
		tank.setId(id);
		tank.setName(name);
		return tank ;// 非要造一個 tank 怎麼辦?
	}
	// 通過等號前宣告的那個變數的型別確定被呼叫的泛型方法返回值的型別
	// 定義一個泛型方法(在返回型別使用泛型、引數中沒有使用)
	// 返回值的型別需要通過宣告接收變數來確定31
	// Tank t = f.create(999, "蘇-890");
	/*public <E> E create(int id, String name){
	return null;
	}*/
}

簡單來說, 可以通過在定義方法的時候指定泛型,讓其作為引數或返回,這樣的方法就可以定義為泛型方法。

迭代器

迭代器就相當於安檢的那個機器,如果有下一個,就繼續,發現違禁產品, remove,如果繼續有下一個,就繼續運轉。 Collection 所有的後代都有 Iterator 這個方法。但是在輸出的時候不建議使用toString。

/**
* 用迭代器迭代 Collection 集合
*/
public class TestIterator { // 迭代器
	public static void main(String[] args) {
		Collection<String> coll = new HashSet<>();// 無序的
		coll.add("孫悟空");34
		coll.add("白龍馬");
		coll.add("豬悟能");
		coll.add("沙悟淨");
		System.out.println( coll );// coll.toString():HashSet 重寫了 toString 方法
		// 獲得迭代器
		Iterator<String> itor = coll.iterator();
		// 迭代器的 hasNext 方法用來判斷是否還有下一個元素
		需要處理
		while(itor.hasNext()){
			String string = itor.next(); // 如果有下一個元素需要處理,就獲得它
			System.out.println(string);
			if( string.equals("白龍馬")){
				itor.remove();// 刪除當前的元素
			}
		}
		System.out.println(coll);
	}
}

foreach

foreach 迴圈,有人稱之為(增強的 for 迴圈),但是這種說法不準 確,我們統一稱之為 foreach 。

/*** 用 for-each 迴圈迭代 Collection 集合
* 除了陣列,只有實現過 java.util.Iterable 介面的型別的物件,
才可以被 foreach 語句處理
*/
public class TestCollection3 {
	public static void main(String[] args) {
		Collection<String> coll = new HashSet<>();// 無序的
		coll.add("孫悟空");
		coll.add("白龍馬");
		coll.add("豬悟能");
		coll.add("沙悟淨");
		System.out.println( coll );// coll.toString();
		/**
		* foreach 語句的冒號之後那個變數對應的型別必須實現過 java.util.Iterable 介面
		* for( 型別 變數名 : Collection 或 陣列){
		*   // 通過變數名來訪問集合或陣列中的每個元素
		* }
		* 注意:冒號之前的變數型別必須由集合(陣列)中的型別確定,冒號之後必須是 Collection 或陣列
		*/
		//JDK1.5+提供的 forEach 迴圈(也就是一些人所謂的 增強的 for 迴圈)
		for (String string : coll) {
			System.out.println(string);
		}
		Object[] array = coll.toArray();
		for (Object object : array) {
			System.out.println(object);
		}
	}
}

Collection體系

可以在這裡看到, Collection介面中有三個主要分支,分別是java.util.Set、java.util.List、java.util.Queue三個主要分支。可以根據這幅圖進行理解Collection體系。

java.util.Set

官方API宣告如下:

public interface Set<E> extends Collection<E>

可以看到依舊是一個介面,但是直接繼承了Collection介面,那麼在使用的過程中依舊要使用子類來構建對應的物件。

官方API描述如下:

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.

具體翻譯如下:

不包含重複元素的集合。更正式的是,集不包含一對元素 e1 和 e2,因此 e1.equal(e2) 最多包含一個空元素。顧名思義,此介面對數學集抽象進行模型。

那麼針對於這個描述來說,也就是說Set集合中是不允許有重複元素的,這點要注意。

java.util.Set 介面的特點:

Set 特點:
預設無序:放入元素時的順序,跟集合中元素存放的順序不同
不可重複:在 Set 集合中,任何一個元素都只能有一個(數學中的集)
可以有 null:在 Set 集合中,預設是可以有 null 元素的

因為Set介面是Collection的介面,那麼具體的方法就不再描述,這裡僅僅描述一下關於子類中的一些操作

java.util.HashSet

HashSet是滿足 Set 所有特點的一個 Set 介面的實現類。那麼就可以使用HashSet充當實現類,從而感受到Set 的特點。示例如下:

/**
* Set 集合的特點:無序、不重複、可以有 null
* 如果是 SortedSet,就是有順序的
*/
public class TestSet {
	public static void main(String[] args) {
		// HashSet 內部儲存資料用的是 HashMap(而 HashMap內部用陣列儲存資料)
		Set<String> set = new HashSet<>();
		boolean r = set.add("hello, world");
		System.out.println(r);
		r = set.add("hello, world");
		System.out.println(r); // false 表示放入失敗,因為已經有這個元素存在
		System.out.println(set.size());
		set.add("google.xyz");
		// 放入的順序 與 顯示的順序不同
		System.out.println(set) ; // set.toString : HashSet 類中重寫了 toString 方法
		set.add(null);
		System.out.println(set);
	}
}

當然,如果想要知道HashSet內部的儲存方式,那麼就可以通過add方法的內部去檢視。注意,當點選內部的時候,要採用HashSet的實現方式,然後可以發現內部使用了HashMap作為儲存方式,並呼叫HashMap的方法,但是歸根到底依舊是採用陣列的方式進行儲存。

注意:HashSet的實現是不同步的。(Note that this implementation is not synchronized.)

HashSet的構造方法

構造方法如下:

HashSet() : Constructs a new, empty set; the backing HashMap instance has default initial capacity (16) and load factor (0.75).構造一個新的空集;返回HashMap例項具有預設初始容量(16)和載入因子(0.75)。
HashSet(int initialCapacity)Constructs a new, empty set; the backing HashMap instance has the specified initial capacity and default load factor (0.75).構造一個新的空集;返回HashMap例項具有指定的初始容量和預設載入因子(0.75)。
HashSet(int initialCapacity, float loadFactor) : Constructs a new, empty set; the backing HashMap instance has the specified initial capacity and the specified load factor.構造一個新的空集;返回HashMap例項具有指定的初始容量和指定的載入因子。
HashSet(Collection<? extends E> c): Constructs a new set containing the elements in the specified collection.構造一個包含指定集合中元素的新集合。

示例如下:

/**
* HashSet 的構造方法
* HashSet() : 預設初始容量是 16,載入因子是 0.75
* HashSet( capacity ) : 初始容量是capacity,載入因子是0.75
* HashSet( capacity , loadFactor ) : 初始容量是 capacity,
載入因子是 loadFactor(是 float 型別)
* HashSet( Collection co ) : 將一個集合包裝成一個 HashSet
*/
public class TestSet2 { // 我們新增的順序 與 我們儲存的順序不同
	public static void main(String[] args) {
		// 注意 載入因子(loadFactor)是 float 型別
		Set<String> names = new HashSet<>(10,0.5f);
		names.add("hello"); // 按住 ctrl + 滑鼠左鍵,進入到HashSet 的 add 方法,發現 HashSet 的本質是 HashMap,並且儲存在陣列中
		names.add("world");
		names.add("你");
		names.add("好");
		System.out.println(names); // [你, world, hello, 好]
		names.add("挺好 ");41
		System.out.println(names); // [你, 挺好 , world, hello,好]
		names.add("不好 ");
		names.add("還行");
		System.out.println(names); // [你, 挺好 , world, 還行,hello, 好, 不好 ]
	}
}

載入因子指的是什麼時候進行擴容,擴容的方法可以進入到add方法中進行檢視。

java.util.TreeSet

TreeSet 是 Set 的另一種實現類, TreeSet 繼承了 SortedSet,而SortedSet 又繼承了 Collection。具體繼承體系如下:

Iterable--> Collection --> Set -->SortedSet ---> NavigableSet ---> TreeSet

首先簡單說一下SortedSet,SortedSet進一步提供關於元素的總體排序的 Set,這些元素使用其“自然順序”進行排序(java.lang.Comparable,自然順序必須實現這個介面)或者根據通常在建立有序 Set 時提供的 Comparator進行排序(java.util.Comparator)。具體的資訊可以去檢視對應的API文件。

TreeSet的特點:

1、一定是有序集合(自然順序 或 用比較器排序 )
2、元素不能重複
3、元素不能為 null
4、不是同步的(不是執行緒安全的)

示例如下:

public class TestTreeSetIsOrder {
	public static void main(String[] args) {
		TreeSet<String> ts = new TreeSet<>();
		ts.add(null);//測試能否存放 null
		System.out.println( ts.add("孫悟空"));//是否有序
		System.out.println( ts.add("沙悟淨"));
		System.out.println( ts.add("豬悟能"));
		System.out.println( ts );
	}
}

TreeSet 內部基於 TreeMap 儲存資料,TreeMap 內部使用紅黑樹(Red-Black tree)儲存資料

紅黑樹( Red-Black tree ) :是一種特殊的二叉樹(資料結構)在某種情況下滿足條件,那麼就是紅色的或者是黑色的。

示例如下:

public class TestTreeSet {
	public static void main(String[] args) {
		// numbers 是放在棧空間的
		// 真正的 TreeSet 物件 是放在堆空間中
		Set<Integer> numbers = new TreeSet<>();
		// 建立一個預設根據元素的自然順序進行排序的有序 Set集合
		numbers.add(250);// 當向集合中新增元素時,就會觸發排序(比較)
		System.out.println(numbers);// number.toString
		numbers.add(125);
		System.out.println(numbers);
		numbers.add(1234);
		System.out.println(numbers);
		numbers.add(1000);
		System.out.println(numbers);
	}
}

TreeSet 是有順序的,但是在每次新增的時候都會進行一次比較,所以應用比較少一些。當資料量足夠大的時候,會非常慢。

支援自然排序就是實現了 Comparable 這個介面,必須是實現了Comparable 的 compareTo 方法。所以排序的方式是由 compareTo方法實現的。

示例如下:

public class Panda implements Comparable<Panda> {
	private Integer id;
	private String name;
	private Date birthdate;// birthday 表示生日 ,出生年月日
	birthdate
	private char gender;
	@Override
	public int compareTo(Panda anotherPanda) {
		// this.id - anotherPanda.id ; // this.id 是準確的,可能造成 anotherPanda 的 id 沒有指定
		int r = 0;
		if (this.id != null && anotherPanda.id != null) {
			System.out.println("比較了");
			// this 小於 another :返回負數
			// this 等於 another :返回 0
			// this 大於 another :返回正數
			r = this.id - anotherPanda.id;
		}
		return -r; // 比較的順序翻轉過來,也就是倒序
		//return r;// 正常比較的結果
	}
	@Override
	public String toString() {
		return "( " + id + ", name=" + name + " ) ";
	}
	// getter and setter
}
/**
* 在 TreeSet 中存放元素,並按照自然順序進行排序
* 所謂自然順序,就是指集合中存放的元素的型別 Panda 實現了Comparable 介面中的 compareTo 方法,
* 而這個 compareTo 方法決定了元素的順序
*/
public class TestTreeSet {
	public static void main(String[] args) {
		Set<Panda> pandas = new TreeSet<>();
		Panda p1 = new Panda();
		p1.setId(999);
		p1.setName("阿寶");
		pandas.add(p1); // 在 Panda類中如果沒有實現Comparable 介面,就會有 ClassCastException 型別轉換異常
		System.out.println(pandas);
		Panda p2 = new Panda();
		p2.setId(888);
		p2.setName("師傅");
		pandas.add(p2);
		System.out.println(pandas);
		Panda p3 = new Panda();
		p3.setId(900);
		p3.setName("二師傅");
		pandas.add(p3);
		System.out.println(pandas);
	}
}

使用比較器進行比較

/**
* 因為 Eagle 沒有實現 java.lang.Comparable 介面,因此 Eagle型別的物件,不支援自然排序
*/
public class Eagle {
	private Integer id;
	private String name;
	private double weight;47
	private Date birthdate;// 出生年月日
	// getter and setter
}
public class TestTreeSet2 {
	public static void main(String[] args) {
		final Calendar cal = Calendar.getInstance(); // 表示系統當前時刻
		// 通過匿名內部類來實現 Comparator介面(實現 compare方法)
		Comparator<Eagle> comparator = new Comparator<Eagle>(){
			@Override
			public int compare(Eagle o1, Eagle o2) {
				/*
				* if( o1 大於 o2 ){
				return 整數;
				}else if( o1 等於 o2){
				return 0;
				}else{
				return 負數;
				}48
				*/
				// 明確比較的東西
				double r = o1.getWeight() - o2.getWeight() ; //這是 double 型別的資料,但是結果要 int,
				// 不建議強制型別轉換,容易有問題比如(6.0-5.5)
				return r == 0 ? 0 : ( r > 0 ? 1 : -1 ) ;
			}
		} ;
		/**
		* 對於不支援自然排序的元素組成的集合,可以通過建立set 時,提供的 Comparator 進行排序
		*/
		Set<Eagle> eagles = new TreeSet<>(comparator); // 比較器相當於天平
		Eagle e1 = new Eagle();
		e1.setId(1);
		e1.setName("Eagle-20");
		cal.set(1974, 8, 9);
		Date birthdate = cal.getTime();
		e1.setBirthdate(birthdate);
		e1.setWeight(6.0);49
		eagles.add(e1);
		Eagle e2 = new Eagle();
		e2.setId(2);
		e2.setName("Eagle-12");
		cal.set(1988, 8, 9);
		e2.setBirthdate(cal.getTime());
		e2.setWeight(5.9);
		eagles.add(e2);
		// 迭代集合
		Iterator<Eagle> iter = eagles.iterator();
		while(iter.hasNext()){
			Eagle e = iter.next();
			System.out.println(e.getId() + " , " + e.getName()+ " ," + e.getWeight());
		}
	}
}

java.util.List

宣告如下:

public interface List<E> extends Collection<E>

描述如下:

An ordered collection (also known as a sequence). The user of this interface has precise control over where in the list each element is inserted. The user can access elements by their integer index (position in the list), and search for elements in the list.(有序的集合(也稱為序列)。此介面的使用者可以精確控制列表中每個元素的插入位置。使用者可以按其整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素)

List的特點:

I、 有序:
	1、元素放入的順序幾位 List 的儲存順序
	2、可以對 List 進行排序操作(自然順序、比較器排序)
	3、因為是有順序的,因此可以根據"索引"操作指定位置的元素
II、 可重複
III、 可以有 null

List 介面除了繼承 Collection 介面的所有方法外,另外還有很多基於索引進行操作的方法。 實現了這幾個介面: List , RandomAccess,Cloneable, Serializable, 其中 RandomAccess, Serializable 是標記介面,實現了 Cloneable 中的 clone 方法,但是這個方法繼承於Object, 是 List 介面的陣列版的實現(內部用陣列儲存資料)、Resizable-array (大小可變陣列)不是說陣列的長度可以變,而是說陣列變數可以指向長度不同的陣列。

因為List是介面,那麼只能通過其子類進行構造物件,常用的子類有:ArrayList、Stack、LinkedList等。這就不再描述List中的方法,因為List繼承了Collection介面,如果有什麼特殊的方法可以進行檢視官方API。

java.util.ArrayList

宣告如下:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

描述如下:

Resizable-array implementation of the List interface. Implements all optional list operations, and permits all elements, including null. In addition to implementing the List interface, this class provides methods to manipulate the size of the array that is used internally to store the list. (This class is roughly equivalent to Vector, except that it is unsynchronized.).

翻譯如下:

可調整列表介面的可調整大小陣列實現。實現所有可選的列表操作,並允許所有元素,包括 null。除了實現 List 介面外,此類還提供操作內部用於儲存列表的陣列大小的方法。(此類大致等效於 Vector,只不過它是未同步的。)

示例如下:

/***
* 放入的順序就是存放的順序
* 通過索引在指定的位置進行新增元素
*/
public class TestArrayList {
	public static void main(String[] args) throws Exception{
		// Constructs an empty list with an initial capacity often.
		// 建立一個初始容量(DEFAULT_CAPACITY)是 10 的空的 list 物件
		List<String> list = new ArrayList<>();
		list.add("孫悟空"); // Collection 中就存在的方法
		list.add("沙悟淨");
		list.add(1,"豬悟能"); // 在 1 處新增新元素
		System.out.println(list); // [孫悟空, 沙悟淨, 豬悟能] 
		// [孫悟空, 豬悟能, 沙悟淨]
		list.set(1, "二師兄"); // 替換 1 處 的元素為新內容
		System.out.println(list); // [孫悟空, 二師兄, 沙悟淨]
		System.out.println(list.size());
	}
}

在這裡要說明的就是除了構造之外,依舊要會的就是對應的add方法的具體過程:

在 執 行 add 方 法 的 時 候 , 會 構 建 數 組 , 具 體 過 程 如 下 :
DEFAULT_CAPACITY = 10
Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
List<String> list = new ArrayList()
this.elementData = EFAULTCAPACITY_EMPTY_ELEMENTDATA;

list.add("孫悟空")做的事情:
ensureCapacityInternal(size + 1);// size 的預設值是 0
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
	minCapacity = Math.max(DEFAULT_CAPACITY, 1); // 確定容量是 10
}
ensureExplicitCapacity(minCapacity);
// minCapacity (10) - 0 > 0
if (minCapacity - elementData.length > 0){
	grow(minCapacity);
}
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
elementData[size++] = e;// 先有 size,後++

ArrayList的構造方法

無參構造 : 
	ArrayList() : this.elementData =DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 什 麼 也 沒 有
做; 無參構造並沒有為陣列提供足夠的空間來存放元素,而是等到第一次 add 時才通過 ensureCapacityInternal 來確保陣列有足夠空間存放元素

ArrayList(int initialCapacity ) :
	this.elementData = new Object[initialCapacity];如果容量不夠了,怎麼辦?找 ensureCapacityInternal

迭代 List 的幾種方法

/***
* 遍歷 List 集合:
* 1、 直接通過索引訪問
* 2、通過 foreach 迴圈
* 3、通過 Iterator60
* 4、通過 ListIterator
*/
public class TestArrayList2 {
	public static void main(String[] args) throws Exception{
		List<String> list = new ArrayList<>();
		list.add("孫悟空"); // Collection 中就存在的方法
		list.add("沙悟淨");
		list.add(1,"豬悟能"); // 在 1 處新增新元素
		list.add(0,"唐三藏");
		System.out.println(list);
		for (int i = 0; i < list.size(); i++) {
			String s = list.get(i); // 取出索引是 i 的元素
			System.out.println(s);
		}
		System.out.println("*********foreach*****************");
		// 迭代集合
		// list 的型別是 List, List 介面繼承了 Collection ,Collection 繼承了 Iterable
		for (String s : list) {
			System.out.println(s);
		}
		System.out.println("*********iterator*****************");
		Iterator<String> itor = list.iterator();
		while(itor.hasNext()){
			String s = itor.next();
			System.out.println(s);
		}
		System.out.println("*********ListIterator*****************");
		ListIterator<String> listItor = list.listIterator();
		while(listItor.hasNext()){
			String s = listItor.next(); // 從前往後
			System.out.println(s);
		}
		System.out.println("從後往前");
		while (listItor.hasPrevious()) {
			String s = listItor.previous();
			System.out.println(s);
		}
	}
}

java.util.Vector

/**
* Vector 是執行緒安全的(形式上, Vector 的大部分方法都有
synchronized)
* ArrayList 不是執行緒安全的(形式上, ArrayList 的所有方法都
沒有 synchronized)
*/
public class TestVector {
	public static void main(String[] args) throws Exception{
		List<String> vector = new Vector<>();
		vector.add("孫悟空"); // Collection 中就存在的方法
		vector.add("沙悟淨");
		vector.add(1,"豬悟能"); // 在 1 處新增新元素
		vector.add(0,"唐三藏");
		System.out.println(vector);
		/***
		* 遍歷 vector
		*/
		for (int i = 0; i < vector.size(); i++) {
		String s = vector.get(i);
			System.out.println(s);
		}
	}
}

ava.util.Stack

/**
* 測試棧的特點:後進先出
*/
public class TestStack {
	public static void main(String[] args) {
		Stack<String> stack = new Stack<>();
		// stack.add("猴子"); // add 可以放,但是不能模擬棧的特點
		stack.push("孫悟空"); // 向棧頂壓入一個元素(入棧)
		System.out.println(stack);
		stack.push("豬悟能"); // 向棧頂壓入元素
		System.out.println(stack); // [孫悟空, 豬悟能]
		String top = stack.peek(); // 檢查棧頂元素(誰在棧頂就返回誰)
		System.out.println(top);
		System.out.println(stack);
		String lastIn = stack.pop() ; // 彈出棧頂元素(誰在頂部就彈出誰,出棧)
		System.out.println(lastIn);
		System.out.println(stack);
	}
}

佇列

佇列分為單端佇列和雙端佇列。

單端佇列

/**
* 單端佇列
* 特點:先進先出(First In First Out)
*/
public class TestQueue {
	public static void main(String[] args) {
		Queue<String> queue = new LinkedList<>();
		queue.add("唐三藏") ; // 新增的時候是新增在佇列的末尾
		System.out.println( queue );
		queue.add("孫悟空"); // 向佇列尾部新增元素
		System.out.println( queue );
		queue.offer("白龍馬");// 向佇列尾部新增元素
		System.out.println( queue );
		// 檢查佇列的頭部
		String head = queue.peek() ; // 只獲取不刪除 獲取到佇列的頭
		System.out.println(head);
		System.out.println(queue);
		head = queue.element(); // 獲取,不刪除
		System.out.println(head);
		System.out.println(queue);
		head = queue.poll() ; // 刪除佇列的頭,並返回;如果佇列為空,則返回 null
		System.out.println(head);
		head = queue.remove(); // 獲取並移除掉 此列的頭部
		System.out.println(head);
		System.out.println( queue);
	}
}

雙端佇列

/**
* 雙端佇列66
* 1、先進先出(First In First Out)
* 2、雙端,兩端都可以新增或刪除元素
*/
public class TestDuque{
	public static void main(String[] args) {
		Deque<String> deque = new LinkedList<>();
		/*** 以 first 為頭,以 last 為尾*/
		// 在其中的一端(last)加入元素
		deque.addLast("白龍馬");
		deque.addLast("豬悟能");
		deque.addLast("沙悟淨");
		System.out.println(deque);
		// 檢視另一端(first)的“頭部”元素
		String first = deque.peekFirst(); // 以first 為頭,以last結尾
		System.out.println(first);
		// 獲取並刪除“頭部”元素
		String head = deque.pollFirst();// deque.removeFirst();
		System.out.println(head);
		System.out.println(deque);
		/*** 以 first 為頭,以 last 為尾*/
		// 在隊尾新增元素
		deque.addFirst("孫悟空");
		System.out.println(deque);
		head = deque.peekLast(); // 檢視佇列頭部的元素
		System.out.println(head);
		System.out.println(deque);
		head = deque.pollLast() ; // 獲取並移除隊頭的元素
		System.out.println(head);
		System.out.println(deque);
	}
}

效能比較

相關推薦

重新開始Java——集合框架Collection

為什麼需要集合框架呢?因為之前使用陣列進行儲存物件的時候,會發現,經常不能夠確定大小的存在,那麼這裡就需要使用集合框架的存在。集合

Java集合框架Collection例項解析

0、集合引入 1)集合的由來? Java是面向物件程式語言,經常需要操作很多物件,必要時需儲存物件(對Java語言而言,儲存的通常是物件的引用,以達到複用或管理等目的),常見容器如陣列和StringBuffer(執行緒安全但效率較低,為了提高效率而

Java集合框架 Collection介面

Collection介面是集合的根介面,它有兩個子介面分別是List介面和Set介面。 Collection介面的具體實類有ArrayList,LinkedList等對集合元素的增,刪,改,查。 使用前需要匯入相應的包import java.util.*; (1)

java集合框架HashCode

封裝 app stringbu result ati des tor 平均值 http 參考http://how2j.cn/k/collection/collection-hashcode/371.html List查找的低效率 假設在List中存放著無重復名稱,沒有順序的

Java基礎知識(JAVA集合框架List與Set)

開發 如果 表數 特點 必須 加鎖 以及 stringbu 不可 List和Set概述數組必須存放同一種元素。StringBuffer必須轉換成字符串才能使用,如果想拿出單獨的一個元素幾乎不可能。數據有很多使用對象存,對象有很多,使用集合存。 集合容器因為內部

Java集合框架三:HashMap原始碼解析 Java集合框架三:HashMap原始碼解析

Java集合框架之三:HashMap原始碼解析     版權宣告:本文為博主原創文章,轉載請註明出處,歡迎交流學習!       HashMap在我們的工作中應用的非常廣泛,在工作面試中也經常會被問到,對於這樣一個重要的集合

Java集合框架HashMap的原始碼解析

1.首先看一下HashMap的繼承關係 java.lang.Object ↳ java.util.AbstractMap<K, V> ↳ java.util.HashMap<K, V> pub

Java集合框架ArrayList原始碼分析

分析: java集合框架主要包括兩種型別的容器,集合(Collection),儲存元素集合,圖(Map),儲存鍵值對對映,而Collection介面又有三種子型別:List,Set,Queue,然後是一些抽象類,最後是一些實現類,常用ArrayList,Lin

常用JAVA集合框架Collection、List、Set、Map)

注意,此實現不是同步的。如果多個執行緒同時訪問一個 ArrayList 例項,而其中至少一個執行緒從結構上修改了列表,那麼它必須 保持外部同步。(結構上的修改是指任何新增或刪除一個或多個元素的操作,或者顯式調整底層陣列的大小;僅僅設定元素的值不是結構上的修改。)應該使用 Collections.synchro

java集合框架02——Collection架構與原始碼分析

Collection是一個介面,它主要的兩個分支是List和Set。如下圖所示:         List和Set都是介面,它們繼承與Collection。List是有序的佇列,可以用重複的元素;而Set是數學概念中的集合,不能有重複的元素。List和Set都有它們各自

JAVA集合框架List、Map、Set之間的選擇~小案例分析

案例分析 案例介紹:簡易撲克牌遊戲。 功能描述: 二:實現洗牌 三:實現發牌 四:得出輸贏 集合(list、set、map)的選擇 既然要比較,我們還是先從JAVA集合的祖先來介紹。 陣列 時間本沒有集合,但有人想要,所以有了集合

Java集合框架Map---HashMap和LinkedHashMap原始碼分析

1、HashMap概述:    HashMap是基於雜湊表的Map介面的非同步實現。此實現提供所有可選的對映操作,並允許使用null值和null鍵。此類不保證對映的順序,特別是它不保證該順序恆久不變。 2、HashMap的資料結構 資料結構中有陣列和連結串列來實現對資料的

java集合框架List集合

list集合是工作中使用最頻繁的一種集合。它是一種有序的集合結構。可以用於儲存重複的元素。通過索引位置我們可以快速的定位元素進行操作。他的實現類主要有三個ArrayList,LinkedList,Vector。 ArrayList ArrayLis是使用最頻繁的一種集合。它是

集合框架collection類詳解

一、集合框架的由來 Java 集合框架主要包括兩種型別的容器,一種是集合(Collection),儲存一個元素集合,另一種是圖(Map),儲存鍵/值對對映。Collection 介面又有 3 種子型別,List、Set和Queue,再下面是一些抽象類,最後是具體實現類,常用

Java集合框架十三-------------HashMap的擴容與執行緒安全問題

HashMap擴容中,hash & oldCap的作用,觀察擴容前和擴容後下標的變化 原來的0101和10101在length=16的時候,通過hash&length-1的方法,計算出來都是0101;但是在擴容後即length=32時,hash&

重新開始Java——Random、Math、BigDecimal、MathContext、System、Runtime

這篇部落格主要描述Random類與Math類,以及一些其他的類。分別講述一些使用方法,以及這個類是做什麼的。 java.util.

重新開始Java——抽象類、介面、內部類

抽象類 為什麼要定義抽象方法:如果定義某個方法時不能確定該方法的具體實現細節; 比如定義 Person 類的 eat 方法時, 不

重新開始Java——異常

異常 ( Exception ) 也叫例外;在 Java 程式語言中,異常就是程 序在執行過程中由於硬體裝置問題、軟體設計錯誤、缺

黑馬程式設計師——Java集合框架(一)迭代器、Collection層次結構等

-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ 集合框架概述 一、什麼是集合框架   1.什麼是集合?   集合是指把具有相同性質的一類東西匯聚成一個整體,簡單說就是指儲存資料的一個容器。集

jdk源碼閱讀筆記java集合框架(四)(LinkedList)

ray private array public 源碼閱讀 jdk源碼閱讀 oid color 解釋 關於LinkedList的分析,會從且僅從其添加(add)方法入手。 因為上一篇已經分析過ArrayList,相似的地方就不再敘述,關註點在LinkedList的特點。 屬