1. 程式人生 > >Java——Link接口(ArrayList,LinkList)和Set接口(HashSet)

Java——Link接口(ArrayList,LinkList)和Set接口(HashSet)

ott 機制 demo 不同的 inside 對象的比較 註意 enter bound

List接口

List接口是Collection接口的子接口,List接口中的抽象方法,有一部分方法和他的父接口Collection是一樣,List接口的自己特有的方法, 帶有索引的功能。

  • 它是一個元素存取有序的集合。例如,存元素的順序是112233。那麽集合中,元素的存儲就是按照112233的順序完成的)。

  • 它是一個帶有索引的集合,通過索引就可以精確的操作集合中的元素(與數組的索引是一個道理)。

  • 集合中可以有重復的元素,通過元素的equals方法,來比較是否為重復的元素。

List接口的常用子類有:

  • ArrayList集合:數據存儲的結構是數組結構。元素增刪慢,查找快,由於日常開發中使用最多的功能為查詢數據、遍歷數據,所以ArrayList是最常用的集合。
  • LinkedList集合:數據存儲的結構是鏈表結構。方便元素添加、刪除的集合。實際開發中對一個集合元素的添加與刪除經常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。

List集合存儲數據的結構

List接口下有很多個集合,它們存儲元素所采用的結構方式是不同的,這樣就導致了這些集合有它們各自的特點,供給我們在不同的環境下進行使用。數據存儲的常用結構有:堆棧、隊列、數組、鏈表。

堆棧,采用該結構的集合,對元素的存取有如下的特點:

  • 先進後出(即,存進去的元素,要在後它後面的元素依次取出後,才能取出該元素)。例如,子彈壓進彈夾,先壓進去的子彈在下面,後壓進去的子彈在上面,當開槍時,先彈出上面的子彈,然後才能彈出下面的子彈。

  • 棧的入口、出口的都是棧的頂端位置

  • 壓棧:就是存元素。即,把元素存儲到棧的頂端位置,棧中已有元素依次向棧底方向移動一個位置。

  • 彈棧:就是取元素。即,把棧的頂端位置元素取出,棧中已有元素依次向棧頂方向移動一個位置。

技術分享圖片

隊列,采用該結構的集合,對元素的存取有如下的特點:

  • 先進先出(即,存進去的元素,要在後它前面的元素依次取出後,才能取出該元素)。例如,安檢。排成一列,每個人依次檢查,只有前面的人全部檢查完畢後,才能排到當前的人進行檢查。

  • 隊列的入口、出口各占一側。例如,下圖中的左側為入口,右側為出口。

技術分享圖片

數組,采用該結構的集合,對元素的存取有如下的特點:

  • 查找元素快:通過索引,可以快速訪問指定位置的元素

  • 增刪元素慢
    • 指定索引位置增加元素:需要創建一個新數組,將指定新元素存儲在指定索引位置,再把原數組元素根據索引,復制到新數組對應索引的位置。如下圖

    • 指定索引位置刪除元素:需要創建一個新數組,把原數組元素根據索引,復制到新數組對應索引的位置,原數組中指定索引位置元素不復制到新數組中。如下圖

技術分享圖片

鏈表,采用該結構的集合,對元素的存取有如下的特點:

  • 多個節點之間,通過地址進行連接。例如,多個人手拉手,每個人使用自己的右手拉住下個人的左手,依次類推,這樣多個人就連在一起了。

  • 查找元素慢:想查找某個元素,需要通過連接的節點,依次向後查找指定元素

  • 增刪元素快:
    • 增加元素:操作如左圖,只需要修改連接下個元素的地址即可。

    • 刪除元素:操作如右圖,只需要修改連接下個元素的地址即可。

技術分享圖片

一、類ArrayList的集合

1、添加元素到指定索引上

/*
 *  add(int index, E)
 *  將元素插入到列表的指定索引上
 *  帶有索引的操作,防止越界問題 java.lang.IndexOutOfBoundsException
 */
public static void function(){
	List<String> list = new ArrayList<String>();
	list.add("abc1");
	list.add("abc2");
	list.add("abc3");
	list.add("abc4");
	System.out.println(list);
	
	list.add(1, "x5456");
	System.out.println(list);
}

2、移除指定索引上的元素

/*
 *  E remove(int index)
 *  移除指定索引上的元素
 *  返回被刪除之前的元素
 */
public static void function_1(){
	List<Double> list = new ArrayList<Double>();
	list.add(1.1);
	list.add(1.2);
	list.add(1.3);
	list.add(1.4);

	list.remove(1.1);   // 移除指定元素,沒有返回值
	Double d = list.remove(0);  // 移除指定索引的值,有返回值(就是被移除的那個值)
	System.out.println(d);
	System.out.println(list);
}

3、替換指定索引上的值

public static void function_2(){
	List<Integer> list = new ArrayList<Integer>();
	list.add(1);
	list.add(2);
	list.add(3);
	list.add(4);
	
	Integer i = list.set(0, 5);	//將0索引的值替換成5,返回值為之前的值
	System.out.println(i);
	System.out.println(list);
}

4、在叠代過程中不能增加集合中的元素

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/*
 *  叠代器的並發修改異常 java.util.ConcurrentModificationException
 *  就是在遍歷的過程中,使用了集合方法修改了集合的長度,不允許的
 */
public class ListDemo1 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("abc1");
		list.add("abc2");
		list.add("abc3");
		list.add("abc4");
		
		//對集合使用叠代器進行獲取,獲取時候判斷集合中是否存在 "abc3"對象
		//如果有,添加一個元素 "ABC3"
		Iterator<String> it = list.iterator();
		while(it.hasNext()){
			String s = it.next();
			//對獲取出的元素s,進行判斷,是不是有"abc3"
			if(s.equals("abc3")){   // 字符串不是基本數據類型,所以不能用==來判斷
				list.add("ABC3");
			}
			System.out.println(s);
		}
	}
}

二、類LinkedList的集合

自身特點: 由鏈表底層實現,查詢慢,增刪快;因為是子類的特有功能,不能多態調用

1、添加元素到鏈表的開頭和結尾

/*
 *  addFirst(E) 添加到鏈表的開頭
 *  addLast(E) 添加到鏈表的結尾
 */
public static void function(){
	LinkedList<String> link = new LinkedList<String>();
	
	link.addLast("xinge");
	
	link.add("abc");
	link.add("bcd");
	
	link.addFirst("x5456");
	System.out.println(link);
}

2、獲取開頭和結尾的值

/*
 * E getFirst() 獲取鏈表的開頭
 * E getLast() 獲取鏈表的結尾
 */
public static void function_2(){
	LinkedList<String> link = new LinkedList<String>();
	link.add("1");
	link.add("2");
	link.add("3");
	link.add("4");

	if(!link.isEmpty()){  // 判斷鏈表集合是否為空,等同於link.size()!=0
		String first = link.getFirst();
		String last = link.getLast();
		System.out.println(first);
		System.out.println(last);
	}
}

3、移除開頭和結尾的值

/*
 *  E removeFirst() 移除並返回鏈表的開頭
 *  E removeLast() 移除並返回鏈表的結尾
 */
public static void function_3(){
	LinkedList<String> link = new LinkedList<String>();
	link.add("1");
	link.add("2");
	link.add("3");
	link.add("4");
	
	String first = link.removeFirst();
	String last = link.removeLast();
	System.out.println(first);
	System.out.println(last);

	System.out.println(link.pop());  // 彈出集合棧頂的元素
}

Set接口

Collection中可以存放重復元素,也可以不存放重復元素,那麽我們知道List中是可以存放重復元素的。那麽不重復元素給哪裏存放呢?那就是Set接口,它裏面的集合,所存儲的元素就是不重復的。

一、HashSet

HashSet集合,采用哈希表結構存儲數據,保證元素唯一性的方式依賴於:hashCode()equals()方法,存儲和取出都比較快,線程不安全,運行速度快。

什麽是哈希表?

哈希表底層使用的也是數組機制,數組中也存放對象,而這些對象往數組中存放時的位置比較特殊,當需要把這些對象給數組中存放時,那麽會根據這些對象的特有數據結合相應的算法,計算出這個對象在數組中的位置,然後把這個對象存放在數組中。而這樣的數組就稱為哈希數組,即就是哈希表。

當向哈希表中存放元素時,需要根據元素的特有數據結合相應的算法,這個算法其實就是Object類中的hashCode方法。由於任何對象都是Object類的子類,所以任何對象有擁有這個方法。即就是在給哈希表中存放對象時,會調用對象的hashCode方法,算出對象在表中的存放位置,這裏需要註意,如果兩個對象hashCode方法算出結果一樣,這樣現象稱為哈希沖突,這時會調用對象的equals方法,比較這兩個對象是不是同一個對象,如果equals方法返回的是true,那麽就不會把第二個對象存放在哈希表中,如果返回的是false,就會把這個值存放在哈希表中。

總結:保證HashSet集合元素的唯一,其實就是根據對象的hashCodeequals方法來決定的。如果我們往集合中存放自定義的對象,那麽保證其唯一,就必須復寫hashCodeequals方法建立屬於當前對象的比較方式。

技術分享圖片

示例:將Person對象中的姓名,年齡,相同數據,看作同一個對象

import java.util.HashSet;

import cn.itcast.demo3.Person;

/*
 *  HashSet集合的自身特點:
 *    底層數據結構,哈希表
 *    存儲,取出都比較快
 *    線程不安全,運行速度快
 */
public class HashSetDemo1 {
	public static void main(String[] args) {
		//判斷對象是否重復,依賴對象自己的方法 hashCode,equals
		HashSet<Person> setPerson = new HashSet<Person>();
		setPerson.add(new Person("a",11));
		setPerson.add(new Person("b",10));
		setPerson.add(new Person("b",10));
		setPerson.add(new Person("c",25));
		setPerson.add(new Person("d",19));
		setPerson.add(new Person("e",17));
		System.out.println(setPerson);
	}
}

Person.java

需要達到要求,就需要重寫Person類的HashCode和equals方法

public class Person {
	private String name;
	private int age;

	/*
	 *  沒有做重寫父類,每次運行結果都是不同整數
	 *  如果子類重寫父類的方法,哈希值,自定義的
	 *  存儲到HashSet集合的依據
	 */
	public int hashCode(){
		return name.hashCode()+age*55;
	}
	//方法equals重寫父類,保證和父類相同
	//public boolean equals(Object obj){}
	public boolean equals(Object obj){
		if(this == obj)
			return true;
		if(obj == null)
			return false;
		if(obj instanceof Person){
			Person p = (Person)obj;
			return name.equals(p.name) && age==p.age;
		}
		return false;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public Person(){}
	
	public String toString(){
		return name+".."+age;
	}
}

二、LinkedHashSet

HashSet存儲的是無序的,LinkedHashSet其中存儲的是有序的

public class LinkedHashSetDemo {
	public static void main(String[] args) {
		Set<String> set = new LinkedHashSet<String>();
		set.add("bbb");
		set.add("aaa");
		set.add("abc");
		set.add("bbc");
                Iterator it = set.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
	}
}        

判斷集合中元素唯一

ArrayList

技術分享圖片

ArrayListcontains方法會使用調用方法時,傳入的元素的equals方法依次與集合中的舊元素所比較,從而根據返回的布爾值判斷是否有重復元素。此時,當ArrayList存放自定義類型時,由於自定義類型在未重寫equals方法前,判斷是否重復的依據是地址值,所以如果想根據內容判斷是否為重復元素,需要重寫元素的equals方法。

HashSet

技術分享圖片

Set集合不能存放重復元素,其添加方法在添加時會判斷是否有重復元素,有重復不添加,沒重復則添加。

HashSet集合由於是無序的,其判斷唯一的依據是元素類型的hashCodeequals方法的返回結果。規則如下:

先判斷新元素與集合內已經有的舊元素的HashCode

  • 如果不同,說明是不同元素,添加到集合。

  • 如果相同,再判斷equals比較結果。返回true則相同元素;返回false則不同元素,添加到集合。

所以,使用HashSet存儲自定義類型,如果沒有重寫該類的hashCodeequals方法,則判斷重復時,使用的是地址值,如果想通過內容比較元素是否相同,需要重寫該元素類的hashcodeequals方法。

Java——Link接口(ArrayList,LinkList)和Set接口(HashSet)