1. 程式人生 > >線性表、佇列和棧

線性表、佇列和棧

集合操作------線性表

1、 List

2、 List的排序

3、 佇列和棧

List

1、 get和set

List除了繼承Collection定義的方法外,還根據其線性表的資料結構定義了一系列方法,其中最常用的就是基於下表的get和set方法:

--E get(int index):獲取集合中指定下標對應的元素,下標從0開始。

--E set(intindex,E element):將給定元素存入給定位置,並將原位置的元素返回。

2、插入和刪除

List根據下標的操作還支援插入與刪除操作。

--add(int index,E element):將給定的元素插入到指定的位置,原位置及後續元素都順序向後移動。

--remove(int index)

:刪除給定位置的元素,並將被刪除的元素返回。

3、ArrayList(變長陣列)和LinkedList(雙向迴圈列表)

List介面是Collection的子介面,用於定義線性表資料結構。可以將List理解為存放物件的陣列,只不過其元素個數可以動態的增加或減少。

List介面的兩個常見實現類為ArrayList和LinkedList,分別用動態陣列和連結串列的方式實現了List介面。

可以認為ArrayList和LinkedList的方法再邏輯上完全一樣,只是在效能上有一定的差別。ArrayList更適合隨機訪問,而LinkedList更適合在頭部和尾部插入和刪除。在效能要求不是特別苛刻的情況下可以忽略這個差別。

public void testList1() {
		Random rand=new Random();
		ArrayList<Integer> list1=new ArrayList<Integer>();
		LinkedList<Integer> list2=new LinkedList<Integer>();
		for(int i=0;i<10000;i++) {
			int j=rand.nextInt();
			list1.add(j);
			list2.add(j);
		}
		//反覆在集合的頭部(0號位置)新增刪除元素
		long t1=System.nanoTime();//納秒
		for(int i=0;i<10000;i++) {
			list1.add(0,5);//將5插入到0號位置,後面元素將順序向後移動,是很消耗效能的。
			list1.remove(0);//將0號元素刪掉,後面元素將順序向前移動,是很消耗效能的。
		}
		long t2=System.nanoTime();
		for(int i=0;i<10000;i++) {
			list2.add(0,5);//插入,按位置插入
			list2.remove(0);//刪除,按位置刪除
		}
		long t3=System.nanoTime();
		System.out.println("順序表消耗的時間:"+(t2-t1));//順序表消耗的時間:26815138
		System.out.println("雙向連結串列消耗的時間:"+(t3-t2));//雙向連結串列消耗的時間:3252906
		//結論:LinkedList頭尾新增和刪除效能好於ArrayList
		
		t1=System.nanoTime();
		for(int i=0;i<10000;i++) {
			int n=list1.get(5000);//獲取指定index位置的元素			
		}
		t2=System.nanoTime();
		for(int i=0;i<10000;i++) {
			int n=list2.get(5000);
		}
		t3=System.nanoTime();
		System.out.println("順序表消耗的時間:"+(t2-t1));//順序表消耗的時間:596195
		System.out.println("雙向連結串列消耗的時間:"+(t3-t2));//雙向連結串列消耗的時間:102743580
		//結論:ArrayList(中部)的讀取效能好於LinkedList
		
		//當不知道該選用哪個List的時候,就選用ArrayList。
	}
public void testList() {
		List<String> list=new ArrayList<String>();
		list.add("Tom");//在尾部新增
		list.add("Jerry");//在尾部新增
		System.out.println(list);//[Tom, Jerry]
		                         //  0     1
		
		String name=list.get(1);//返回1號位置的元素
		System.out.println(name);//Jerry
		
		String one=list.set(1, "Nemo");//set 將1號位置的Jerry換成Nemo,並且將Jerry返回
		System.out.println(one);//Jerry
		System.out.println(list);//[Tom, Nemo]
		
		list.add(1, "Jerry");//在1的位置新增Jerry,其餘的元素順序後移
		System.out.println(list);//[Tom, Jerry, Nemo]
		
		list.remove(0);//將0號位置的元素刪除,其餘元素順序向前移動
		System.out.println(list);//[Jerry, Nemo]
		
		//以上方法都是與index(位置)有關的操作方法,是List擴充套件的方法,此外List還從Collection繼承了全部的集合操作方法
	}

4、 subList

List的subList方法用於獲取子List。需要注意的是,subList獲取的List與原List佔有相同的儲存空間,對subList的操作會影響原List。

List<E>subList(int fromIndex,int toIndex);fromIndex和toIndex是擷取List的首尾下標,包括fromIndex,不包括toIndex。

public void testSublist() {
		List<String> cards=new ArrayList<String>();
		cards.add("黑桃2");//0
		cards.add("紅桃2");//1
		cards.add("紅桃10");//2
		cards.add("梅花10");//3
		cards.add("方片10");//4
		cards.add("黑桃10");//5
		cards.add("黑桃3");//6
		List<String> bomb=cards.subList(2, 6);//炸彈是從2到6,包括2,不包括6
		System.out.println(bomb);//[紅桃10, 梅花10, 方片10, 黑桃10]
		bomb.clear();
		System.out.println(cards);//[黑桃2, 紅桃2, 黑桃3]
		//list和sublist共享一塊記憶體空間
	}

總結List

List繼承了Collection,擴充套件了與位置有關的操作方法,實現類有兩個:ArrayList和LinkedList,兩個實現類的方法一致,都是List。任何時候,需要使用集合,但是不能確定使用哪個實現類的時候,首先選擇ArrayList。

5、 List轉換為陣列

List的toArray方法用於將集合轉換為陣列。但實際上該方法是在Collection中定義的,所以所有的集合都具備這個功能。

其有兩個方法:

   Object[] toArray;這個只能轉換成Object型別的陣列

public void testToArry1() {
		//Java先編譯後執行,編譯階段沒有物件,只有變數的型別,執行期間才有物件,物件可能是多型的。
		Collection<String> list=new ArrayList<String>();
		list.add("Tom");
		list.add("Andy");
		list.add("Nemo");
		list.add("Jerry");
		
		Object[] names=list.toArray();
		//names的每個元素變數型別是Object型別,元素變數型別不是String型別!不是String[]!
		//[Object[0],Object[1],...]
		System.out.println(Arrays.toString(names));//[Tom, Andy, Nemo, Jerry]
		//返回“執行期間”物件的型別!
		System.out.println(names[0].getClass().getName());//java.lang.String						
	}

   <T>T[] toArray(T[] a);可以轉換成指定型別的陣列,如果這個陣列的長度小於陣列元素的個數,則返回新陣列。如果陣列的長度大於陣列元素的個數,返回原陣列,沒有值的陣列位置存放null

public void testToArray2() {
		Collection<String> list=new ArrayList<String>();
		list.add("Tom");
		list.add("Andy");
		list.add("Nemo");
		list.add("Jerry");
		
		String[] name1=new String[2];//新建一個String型別的陣列,長度為2,但是新增的元素個數是4,陣列長度不夠,則返回新陣列
		String[] name2=new String[6];//新建一個String型別的陣列,長度為6,但是新增的元素個數是4,沒有值的填充0,返回原陣列
		String[] name3=new String[list.size()];//最理想的情況下
		
		//返回的是String[]型別的陣列
		name1=list.toArray(name1);//返回新陣列
		name2=list.toArray(name2);//返回原陣列
		name3=list.toArray(name3);//返回原陣列
		
		System.out.println(Arrays.toString(name1));//[Tom, Andy, Nemo, Jerry]
		System.out.println(Arrays.toString(name2));//[Tom, Andy, Nemo, Jerry, null, null]
		System.out.println(Arrays.toString(name3));//[Tom, Andy, Nemo, Jerry]
	}

6、 陣列轉換為List

Arrays類中提供了一個靜態方法asList,使用該方法我們可以將一個數組轉換為對應的List集合。返回的List的集合元素型別由傳入的陣列的元素型別決定。並且需要注意的是,返回的集合我們不能對其進行增刪元素,否則會丟擲異常。並且對集合的元素進行修改會影響陣列對應的元素。

public void testAsList() {
		//陣列轉換為List
		String[] names= {"Tom","Andy","Jerry"};
		//list是靜態的list,長度不能改變,也就是不能呼叫add和remove
		List<String> list1=Arrays.asList(names);
		//靜態list和name共享同一個記憶體空間,改陣列,list也會被更改
		//不是ArrayList也不是LinkedList,這個list的型別是內部類
		System.out.println(list1.getClass().getName());//java.util.Arrays$ArrayList是一個內部類
		System.out.println(list1);//[Tom, Andy, Jerry]
		//list1.remove(0);//會出現執行異常,不支援的操作
		//list1.add();
		
		//陣列轉換為List的第二種方法
		List<String> list2=new ArrayList<String>();
		//list2和name不共享記憶體空間,更改陣列,對list沒有影響
		Collections.addAll(list2, names);//將names中所有元素新增到list2中
		System.out.println(list2.getClass().getName());//java.util.ArrayList
		System.out.println(list2);
		
		//靜態List與陣列names共享相同的儲存空間
		//修改陣列names影響線性表List
		names[1]="Lee";
		System.out.println(list1);//[Tom, Lee, Jerry]
		System.out.println(list2);//[Tom, Andy, Jerry]
	}

List的排序:

排序:按照一定的大小順序排序

比較大小:比較兩個元素的大小

public void testStringCompare() {
		//String型別如何比較大小
		//在String類中有一個方法compareTo
		String s1;
		String s2;
		s1="A";
		s2="B";
		if(s1.compareTo(s2)>0) {
			System.out.println("s1大於s2");
		}
		if(s1.compareTo(s2)<0) {
			System.out.println("s1小於s2");
		}
		if(s1.compareTo(s2)==0) {
			System.out.println("s1等於s2");
		}
		//String是可以比較大小的,包含預設
		//比較大小的方法compareTo
		//Java中凡是可以預設比較大小的型別,都有compareTo,比如:包裝類Date
		//有compareTo方法的類,稱為可以自然的類
		//compareTo是compareable介面定義的方法,可以自然排序的類都實現了這個介面
		//凡是實現compareable,實現了compareTo方法的類,都可以進行自然(預設)排序。
	}

1、 Collections.sort方法實現排序

Collections是集合的工具類,它提供了很多便於我們操作集合的方法,其中就有用於集合排序的sort方法。

該方法的定義為:

--voidsort(List<T> list):該方法的作用是對給定的集合元素進行自然排序。

List的每個元素要有預設比較規則,sort根據元素的預設比較規則進行排序,若沒有預設的比較規則,則不能排序。

    public void testSort1() {
		List<Integer> list=new ArrayList<Integer>();
		Random ran=new Random();
		for(int i=0;i<10;i++) {
			list.add(ran.nextInt(100));
		}
		System.out.println(list);//[16, 44, 85, 60, 19, 98, 31, 57, 78, 97]
		
		Collections.sort(list);
		System.out.println(list);//[16, 19, 31, 44, 57, 60, 78, 85, 97, 98]
	}
	
	public void testSort2() {
		//預設的自然排序:呼叫比較規則compareTo
		List<String> names=new ArrayList<String>();
		Collections.addAll(names, "Tom","Jerry","Andy","John");
		System.out.println(names);//[Tom, Jerry, Andy, John]
		Collections.sort(names);
		System.out.println(names);//[Andy, Jerry, John, Tom]
	}

2、 Comparator:自定義排序

一旦Java類實現了Comparable介面,其比較邏輯就已經確定;如果希望在排序的操作中臨時指定比較規則,可以採用Comparable介面回撥的方式。Comparable介面要求實現類必須重寫其定義的方法:

--int compare(To1,T o2)

該方法的返回值要求:

--若o1>o2,則返回值應>0

--若o1<o2,則返回值應<0

--若o1==o2,則返回值應為0

利用方法:

      Collections.sort(list,構造器)

就可以進行任意規則的排序

        public void testSort3() {
		//自定義的比較器:按照字元長度比較大小
		Comparator<String> byLength=new Comparator<String>() {
			public int compare(String o1,String o2) {
				return o1.length()-o2.length();
			}
		};//內部類
		String s1="abcde";
		String s2="好好學習we";
		System.out.println(byLength.compare(s1,s2));
		
		
		List<String> names=new ArrayList<String>();
		Collections.addAll(names, "Tom","Jerry","Andy","John");
		System.out.println(names);//[Tom, Jerry, Andy, John]
		//對names按照字串長度比較大小進行排序,這是Andy和John誰在前面都可以,因為長度一樣
		Collections.sort(names, byLength);
		System.out.println(names);//[Tom, Andy, John, Jerry]
		//sort方法封裝了排序演算法,按照從小到大排序,是依照byLength中的compare()方法的結果比較大小排序
	}

	public void testSort4() {
		//實現按字串倒序排序的演算法
		String s1="abc";
		String s2="zde";
		//s1.compareTo(s2)<0
		Comparator<String> byValue=new Comparator<String>() {
			public int compare(String o1,String o2) {
				return -(o1.compareTo(o2));//按照z到a的順序排序
			}
		};
		System.out.println(s1.compareTo(s2));//-25
		System.out.println(byValue.compare(s1, s2));//25
		
		List<String> list=new ArrayList<String>();
		Collections.addAll(list, "abc","d","f","z");
		System.out.println(list);//[abc, d, f, z]
		Collections.sort(list, byValue);
		System.out.println(list);//[z, f, d, abc]
	}
	
	public void testSort5() {
		Comparator<Person> byAgeHeight=new Comparator<Person>() {
			public int compare(Person o1, Person o2) {
				////先按照年齡比較,年齡如果相同就按照身高比較
				if(o1.age==o2.age) {
					return o1.height-o2.height;
				}
				return o1.age-o2.age;
			}
		};
		//先按照年齡比較,年齡如果相同就按照身高比較
		List<Person> list=new ArrayList<Person>();
		list.add(new Person("Tom",4,35));
		list.add(new Person("Jerry",10,22));
		list.add(new Person("Andy",20,11));
		list.add(new Person("Nemo",2,35));
		list.add(new Person("Lee",4,20));
		System.out.println(list);//[Tom:4:35, Jerry:10:22, Andy:20:11, Nemo:2:35, Lee:4:20]
		Collections.sort(list, byAgeHeight);
		System.out.println(list);//[Nemo:2:35, Lee:4:20, Tom:4:35, Jerry:10:22, Andy:20:11]
	}
	class Person{
		String name;
		int age;
		int height;
		public Person(String name,int age,int height) {
			this.name=name;
			this.age=age;
			this.height=height;
		}
		public String toString() {
			return name+":"+age+":"+height;
		}
	}

關於自定義排序:

1、Comparable可比較的,這個介面不常用,是預設比較規則介面,實現這個介面的類可以進行“自然排序”。Java API中有些型別實現了這個介面:String、包裝類、時間等。實現這個介面必須保證:equals  compareTo  hashCode的一致性,當兩個物件equals結果為true的時候,compareTo結果為0,同時具有相同的hashCode。

2、Comparator:比較器,用於實現某兩個物件的自定義比較大小,沒有限定,可以按照任何屬性和演算法比較,是常用的比較規律,可以利用這個比較器,進行任意的排序。

3、Java自帶的排序演算法都是升序排序,可以利用比較規則影響排序結果。

Collections

Collections是集合的工具類,包含大量的集合工具方法(演算法):填充Collections.addAll(),排序Collections.sort()、查詢、二分查詢等。去java2s.com這個網站中檢視具體的。

佇列和棧:

1、Queue

佇列遵循先進先出原則,一端進,一端出。

JDK提供了Queue介面,同時使得LinkedList實現了該介面。選擇LinkedList實現Queue的原因在於Queue經常要進行新增和刪除的操作,而LinkedList在這方面效率高。

Queue介面主要方法如下:

1、boolean offer(E e):將一個物件新增至隊尾,如果新增成功則返回true

2、E poll():從隊首刪除並返回一個元素。

3、E peek():返回隊首的元素(但不刪除)。

4、queue.isEmpty();

5、queue.size();

public void testQueue() {
		Queue<String> queue=new LinkedList<String>();
		queue.offer("a");
		queue.offer("b");
		queue.offer("c");
		System.out.println(queue);//[a,b,c]
		String str=queue.peek();//返回隊首的元素
		System.out.println(str);//a
		while(queue.size()>0) {
			str=queue.poll();//從隊首刪除並返回一個元素。
			System.out.print(str+" ");//a b c
		}				
	}

1、 Deque

Deque是Queue的子介面,定義了所謂的雙端佇列,即從佇列的兩端分別可以入隊(offer)和出隊(poll),LinkedList實現了該介面。

2、

如果將Deque限制為只能從一端入隊和出隊,則可實現棧(Stack)的資料結構,對於棧而言,入棧稱之為push,出棧稱之為pop。棧遵循先進後出的原則。

    public void testStack() {
		Deque<String> stack=new LinkedList<String>();
		stack.push("a");
		stack.push("b");
		stack.push("c");
		System.out.println(stack);//[c,b,a]
		String str=stack.peek();//c
		System.out.println(str);
		while(stack.size()>0) {
			str=stack.pop();
			System.out.print(str+" ");//c b a
		}
	}

棧的方法:

1、stack.push();

2、stack.pop();

3、stack.peek();

4、stack.isEmpty();

5、stack.empty();

6、stack.size();