2013年10月9日

JAVA 集合架構與泛型

集合架構與泛型


  • 集合

當我們使用陣列時,作大量資料的變動會需要做很多不必要的運算動作。所以JAVA設計集合的資料結構去做這些物件大量的變更。在集合的操作上,會將型別轉為Object存入集合中。

(1)集合繼承關係



(2) Collection方法
用來對集合的物件的基本操作

Name Description
boolean add(E e)  將物件加入到目前集合中
boolean  addAll(Collection<? extends E> c)  將集合物件加入到目前集合中
void clear()  移除所有的集合元素
boolean contains(Object o)  如果集合有符合o元素回傳true,否回傳false
boolean containsAll(Collection<?> c)  如果該集合有符合傳入集合所有的元素者回傳true,否回傳false
boolean equals(Object o)  比較物件是否相等
int hashCode()  回傳 hash code 
boolean isEmpty()  如果該集合沒有任何物件回傳true
 Iterator<Eiterator()  回傳iterator物件集合,可以使用iterator的物件方法
boolean  remove(Object o)  從目前物件集合移除符合傳入的物件
boolean   removeAll(Collection<?> c)  從目前物件集合移除符合傳入的物件
int size()  傳回此集合所有元素的個數
Object[] toArray()   回傳陣列物件
<T> T[] 
toArray(T[] a)
 
回傳陣列物件                      

程式
/*****************************************************************************************************************
*   name : List 範例
*   description: 實作add, isEmpty, remove, size, iterator方法
*   author : Bryant
*****************************************************************************************************************/
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Product {

public String name ;
public String id ; 
public  Double price ; 

public Product(String name, String id, Double price){
this.name = name ; 
this.id = id ; 
this.price = price ;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "id:" + this.id + "\t name:"+this.name +"\t price:" + this.price +"\t\n"  ;
}

public static void main(String[] args) {
// TODO Auto-generated method stub

List<Product> list = new LinkedList<Product>() ;
System.out.println("判斷是否為空物件:"+ list.isEmpty());

Product p1 = new Product("ipad", "K001",  299d) ; 
Product p2 = new Product("iphone", "K101",  299d); 
Product p3 = new Product("ipod", "K002",  299d); 
Product p4 = new Product("imac", "K015",  500d); 
list.add(p1) ;
list.add(p2) ;
list.add(p3) ;
list.add(p4) ;

System.out.println("加入ipad, iphone的產品資料:\n" + list.toString());
System.out.println("產品總計:"+list.size());

//移除資料例子
if(list.remove(p1)){
System.out.println("移除ipad的產品資料:"+ p1);
}else{
System.out.println("資料庫沒有資料");
}

System.out.println("目前的資料:\n" + list.toString());

//使用iterator
System.out.println("使用iterator集合走訪集合:");
Iterator<Product> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next()); //取出元素
}

}

}
  • Iterator 介面
Name Description
boolean hasNext() 是否有下一個元素,若有回傳true
Obejct next() 回傳下一個Object元素並指定下一個元素
void remove() 移除目前的元素
  • Enumeration介面
Name Description
boolean  hasMoreElements() 是否有下一個Enumeration元素
Object nextElement() 回傳下一個Object元素並指定下一個元素
  • Set 介面
set介面是繼承Collection介面,set再加入元素時,會利用equal()方法來判斷加入元素內容使否重複,所以set不可以重複。實作: LinkedHashSet, HashSet, TreeSet

public interface Set extends Collection

(1) HashSet
HashSet元素不允許重複,利用hashCode()來當作 index-value,

/*****************************************************************************************************************
*   name : HashSet範例
*   description: 實作當我們建立重複的字串物件,結果hashSet不允許重複的元素
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {
// HashSet 不允許重複的元素

HashSet<String> hset = new HashSet<String>() ; 
hset.add(new String("AAA")) ;
hset.add(new String("BBB")) ;
hset.add(new String("AAA")) ;
hset.add(new String("AAA")) ;

System.out.println("HashSet元素:");
for(String p : hset){
System.out.println(p);

}
Result:
HashSet元素:
BBB
AAA

(2) LinkedHashSet類別
它是利用資料結構的doubly-linked相互連結,利用next、previous節點指向其他元素,它具有順序性。

/*****************************************************************************************************************
*   name : 
LinkedHashSet範例
*   description: 建立雙鏈結的集合
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {

System.out.println("LinkedHashSet元素:");
LinkedHashSet<Product> lhset = new LinkedHashSet<Product>() ; 
lhset.add(new Product("ipad", "A001", 299.0)) ;
lhset.add(new Product("imac", "A101", 499.0)) ;
lhset.add(new Product("ipod", "A021", 100.0)) ;
 
for(Product p : lhset){
System.out.println(p);

}
}

(3) SortedSet介面
利用紅黑數的資料結構來實作,
/*****************************************************************************************************************
*   name : 
SortedSet範例
*   description: 建立TreeSet具有排序性
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {

//SortedSet 搜尋件
System.out.println("SortedSet元素:");
TreeSet<String> sts = new TreeSet<String>() ; 
sts.add("A") ; 
sts.add("F") ; 
sts.add("B") ; 
sts.add("E") ; 
Iterator<String> it = sts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}

  • List介面
List集合使用的方式相似陣列的使用方式,利用索引來找到該元素。它也是繼承collection介面,所以可以利用Collections的類別來做操作。

Name Description
boolean add(E e)  將物件加入到目前集合中
boolean  addAll(Collection<? extends E> c)  將集合物件加入到目前集合中
Object get(int index) 利用索引取出物件
int indexOf(Object o) 回傳物件的索引值,如果沒有找到回傳-1,否則回傳該物件的索引值
Object remove(int index) 回傳被移除的物件

List<E> subList(int from, int to)
回傳子List                     

(1)ArrayList

/*****************************************************************************************************************
*   name : List 範例
*   description: 實作List方法
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {
// TODO Auto-generated method stub

ArrayList<Product> aList = new ArrayList<Product>() ; 
aList.add(new Product("ipad", "A001", 299.0)) ;
aList.add(new Product("imac", "A101", 499.0)) ;
aList.add(new Product("ipod", "A021", 100.0)) ;

System.out.println("元素列表:");
for(int i=0 ; i < aList.size();i++){
System.out.println(aList.get(i));
}

int index = aList.indexOf(aList.get(1));
System.out.println("imac index:"+index);

aList.remove(aList.get(1));

System.out.println("元素列表:");
for(int i=0 ; i < aList.size();i++){
System.out.println(aList.get(i));
}

}


(2) Vector
當你需要考慮到資料需要做同步時,我們可以利用Vector作物件的集合。

/*****************************************************************************************************************
*   name : Vector 範例
*   description: 實作vector
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {
//Vector
System.out.println("建立Vector集合:");
Vector<String> vec = new Vector<String>() ; 
vec.add("Jack") ; 
vec.add("Bryant") ; 
vec.add("VIVI");

Iterator<String> it = vec.iterator() ; 
while(it.hasNext()){
System.out.println(it.next());
}
}


  • Queue

Queue是一種先進先出(FIFO, first-in-first-out)的集合,利用offer()和poll()兩個方法

Name Description
boolean offer(E e) 加入一個特定的物件,成功為true,否為false
E peek() 依序取出元素,但不移除該元素,當為空物件,回傳null
E element() 依序取出元素
E poll() 依序取出元素
E remove() 移除元素                     

/*****************************************************************************************************************
*   name : Queue範例
*   description: Queue的走訪實作
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {
Queue<String> loginSequence = new LinkedList<String>();
 loginSequence.add("Jack");
 loginSequence.add("Hask");
 loginSequence.add("Lkk");
 loginSequence.add("Lennon");
 System.out.println("The login sequence is: " + loginSequence);
 
 while(!loginSequence.isEmpty())
     System.out.println( loginSequence.poll());
  }

}


  • Map介面
Map是一種key-value的方式去儲存物件集合,但是key不可重複,Map: Hashtable, HashMap

Name Description
boolean add(E e)  將物件加入到目前集合中
boolean  addAll(Collection<? extends E> c)  將集合物件加入到目前集合中
Object get(int index) 利用索引取出物件
int indexOf(Object o) 回傳物件的索引值,如果沒有找到回傳-1,否則回傳該物件的索引值
Object remove(int index) 回傳被移除的物件

List<E> subList(int from, int to)
回傳子List                     



(1)Hashtable
Hashtable的鍵與值不得置入null。
/*****************************************************************************************************************
  name : Hashtable範例
*   description: 實作
Hashtable,且鍵為null時
*   author : Bryant

*****************************************************************************************************************/
public static void main(String[] args) {
// Hashtable
Hashtable<String, String> htable = new Hashtable<>();
htable.put("K01", "ASUS") ;
htable.put(null, "ACER") ;
htable.put("K02", "APPLE") ;
htable.put("K04", "HP") ;
   System.out.println("電腦品牌:");
System.out.println("K01 =>" + hmap.get("K01"));
System.out.println("K01 =>" + hmap.get("K02"));

}

結果:

Exception in thread "main" java.lang.NullPointerException
at java.util.Hashtable.hash(Unknown Source)
at java.util.Hashtable.put(Unknown Source)

at Example.MapEx.main(MapEx.java:25)



(2)HashMap
hashmap可以容許鍵或值是null,它是一種thread-safe

/*****************************************************************************************************************
*   name : 
HashMap範例
*   description: 實作HashMap,且鍵為null時
*   author : Bryant

*****************************************************************************************************************/
public static void main(String[] args) {
HashMap hmap = new HashMap<>(); hmap.put("K01", "ASUS") ; hmap.put("K02", "ACER") ; hmap.put("K03", "APPLE") ; hmap.put("K03", "HP") ; //會蓋掉前面的值 hmap.put(null, "ACER") ; System.out.println(hmap.toString()); System.out.println("K01 =>" + hmap.get("K01")); System.out.println("K01 =>" + hmap.get(null));
}

結果:
{null=ACER, K03=HP, K02=ACER, K01=ASUS}
K01 =>ASUS
K01 =>ACER

(3) LinkedMap
LinkedMap會依照次序將元素加入集合中,

/*****************************************************************************************************************
*   name : 
LinkedMap範例
*   description: 
 實作LinkedMap,且鍵為null時

*   author : Bryant

*****************************************************************************************************************/
public static void main(String[] args) {
//LinkedHashMap
LinkedHashMap<String, Product[]> linkedMap = new LinkedHashMap<String,Product[]>() ;

Product p1 = new Product("ipad", "K001",  299d) ; 
Product p2 = new Product("iphone", "K101",  199d); 
Product p3 = new Product("ipod", "K002",  399d); 
Product p4 = new Product("imac", "K015",  500d); 
Product[] group1 = {p1, p2} ; 
Product[] group2 = {p3, p4} ; 

linkedMap.put("建國路分店", group1) ;
linkedMap.put("中正路分店", group2) ;
System.out.println("建國路分店 =>" +  Arrays.toString(linkedMap.get("建國路分店")));
System.out.println("中正路分店 =>" +  Arrays.toString(linkedMap.get("中正路分店")));
}

(4) SortedMap介面
TreeMap實作SortedMap具有排序性

/*****************************************************************************************************************
*   name : 
SortedMap範例
*   description: 實作TreeMap依靠鍵值作排序
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {
// SortedMap
TreeMap<Integer, String> treeMap = new TreeMap<Integer, String>() ; 
treeMap.put(1, "Jack");
treeMap.put(6, "Vivi");
treeMap.put(2, "Jock");
treeMap.put(5, "Blackhaker");
System.out.println("SortedMap : ");
System.out.println(treeMap.toString());

}

結果:
SortedMap : 

{1=Jack, 2=Jock, 5=Blackhaker, 6=Vivi}


  • Comparable and Comparator Interfaces
Compareable和Comparator介面常用來比較相似的物件,例如當我有一組的產品的物件,你如何去比較這些產品物件。

int compareTo(Element that)
return 1 if current object > passed object
return 0 if current object == passed object
return -1 if current object < passed object

(1) 實作Comparable
在實作Comparable時,必須要override compareTo的方法,

/*****************************************************************************************************************
*   name : 
Comparable範例
*   description: 實作產品排序由大到小
*   author : Bryant
*****************************************************************************************************************/
public class Product implements Comparable<Product>{
public String name ;
public String id ; 
public  Double price ; 

public Product(String name, String id, Double price){
this.name = name ; 
this.id = id ; 
this.price = price ;
}

@Override
public String toString() {
// TODO Auto-generated method stub
return "id:" + this.id + "\t name:"+this.name +"\t price:" + this.price +"\t\n"  ;
}

public static void main(String[] args) {

List<Product> list = new LinkedList<Product>() ;
Product p1 = new Product("ipad", "K001",  299d) ; 
Product p2 = new Product("iphone", "K101",  299d); 
Product p3 = new Product("ipod", "K002",  299d); 
Product p4 = new Product("imac", "K015",  500d); 
list.add(p1) ;
list.add(p2) ;
list.add(p3) ;
list.add(p4) ;

Iterator<Product> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next()); //取出元素
}

System.out.println("排序後的物件");
TreeSet<Product> tree = new TreeSet<>(list) ; 
 it = tree.iterator();
while(it.hasNext()){
System.out.println(it.next()); //取出元素
}
}

@Override
public int compareTo(Product that) {
// TODO Auto-generated method stub
if(this.id.compareTo(that.id)>=1)
return -1;
else if(this.id.compareTo(that.id)==0)
return 0 ; 
else
return 1 ;
}
}


(2)實作Comparator
如果你不想要更改Product物件的比較方式,你可以實作comparator的方式去實作排序。


/*****************************************************************************************************************
*   name : Comparator
範例
*   description: 建立排序產品價錢有小到大
*   author : Bryant
*****************************************************************************************************************/
public class ProdcutComparator implements Comparator<Product>{


@Override
public int compare(Product o1, Product o2) {
// TODO Auto-generated method stub
return o1.price.compareTo(o2.price);
}
}  

/*****************************************************************************************************************
*   name : 
Comparator範例
*   description: 
 建立排序產品價錢有小到大
*   author : Bryant
*****************************************************************************************************************/
public class Product implements Comparable<Product>{

public String name ;
public String id ; 
public  Double price ; 
public Product(String name, String id, Double price){
this.name = name ; 
this.id = id ; 
this.price = price ;
}

@Override
public String toString() {
// TODO Auto-generated method stub
return "id:" + this.id + "\t name:"+this.name +"\t price:" + this.price +"\t\n"  ;
}

public static void main(String[] args) {

List<Product> list = new LinkedList<Product>() ;
Product p1 = new Product("ipad", "K001",  299d) ; 
Product p2 = new Product("iphone", "K101",  199d); 
Product p3 = new Product("ipod", "K002",  399d); 
Product p4 = new Product("imac", "K015",  500d); 
list.add(p1) ;
list.add(p2) ;
list.add(p3) ;
list.add(p4) ;

Iterator<Product> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next()); //取出元素
}

System.out.println("排序後的物件");
Collections.sort(list, new ProdcutComparator());
it = list.iterator();
while(it.hasNext()){
System.out.println(it.next()); //取出元素
}
}

@Override
public int compareTo(Product that) {
// TODO Auto-generated method stub
if(this.id.compareTo(that.id)>=1)
return -1;
else if(this.id.compareTo(that.id)==0)
return 0 ; 
else
return 1 ;
}
}
結果:
id:K001 name:ipad price:299.0

id:K101 name:iphone price:199.0

id:K002 name:ipod price:399.0

id:K015 name:imac price:500.0

排序後的物件
id:K101 name:iphone price:199.0

id:K001 name:ipad price:299.0

id:K002 name:ipod price:399.0

id:K015 name:imac price:500.0



(3)比較
Comparable Comparator
通常會使用預設的比較方式 實作自訂的比較方式
實作Comparable方法,不需要跟物件類別分開 實作d


  • Collections類別
Collections為公用函式,收集在java.lang.Object,可作為集合資料的處理。

Name Description
boolean addAll( Collection <? super T> c, T elements) 加入集合
Queue asLifoQueue(Deque<T> deque) 回傳Deque集合
int binarySearch(List<? extends Comparable<? super T>> list, T key) 搜尋串列回傳符合該物件的位置
int binarySearch(List<? extends Comparable<? super T>> list, T key,Comparator<? super T> c ) 搜尋串列回傳符合該物件的位置,第三個參數可以使用自定排序方法
List<E> checkedList(List<E> list, Class<E> type) 傳回隨機的Map集合
Map<K,V> checkedMap(Map<K,V> m, Class<K> keyType, Class<V> valueType) 傳回隨機的List集合
Set<E>checkedSet(Set<E> s, Class<E> type) 傳回隨機的Set集合
SortedMap<K,V>
  checkedSortedMap(SortedMap<K,V> m, Class<K> keyType, Class<V> valueType) 
傳回隨機的SortedMap集合
void copy(List<? super T> dest, List<? extends T> src) 複製List另一個List集合
Enumeration<T>  enumeration(Collection<T> c)  回傳enumeration
ArrayList<T> list(Enumeration<T> e)  回傳一個ArrayList集合
max(Collection<? extends T> coll) 找出最大值
max(Collection<? extends T> coll, Comparator<? super T> comp) 找出最大值,第二個參數可以使用自定排序方法
min(Collection<? extends T> coll) 找出最小值
min(Collection<? extends T> coll, Comparator<? super T> comp) 找出最小值,第二個參數可以使用自定排序方法
static void reverse(List<?> list)  反向排序
static void shuffle(List<?> list)  亂數排序
void sort(List<T> list)  排序
void sort(List<T> list, Comparator<? super T> c)                        
  • Arrays 類別








  • Generics 泛型
(1) 基本泛型概念

Java 5.0 所提供的泛型是指廣泛的使用型別,在5.0之前通常會用Object來解決需要泛型的實作,會用到泛型來解決問題,通常是一個邏輯或是程序相同,只是資料型態不同而已,在5.0之前可以利用Object來解決這個問題或是利用泛型不需要因為資料型態不同而去實作很多的類別檔案出來

/*****************************************************************************************************************
*   name : Object範例
*   description: 使用Object來當作泛行使用
*   author : Bryant
*****************************************************************************************************************/
public class Operator {

private Object obj ;
public void setObj(Object obj){
this.obj  = obj;
}
public Object getobj(){
return this.obj ;
}

public static void main(String[] args) {

Operator op1 = new Operator() ; 
Operator op2 = new Operator() ; 
op1.setObj(10);
op2.setObj(20f); 
Integer i1 = (Integer)op1.getobj() ; 
Integer i2 = (Integer)op2.getobj() ;    //出現錯誤,因為20f為浮點數
Integer sum = i1 + i2 ;
System.out.println("sum:"+sum);

}
}
Exception in thread "main" java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.Integer
at Example.Operator.main(Operator.java:21)

/*****************************************************************************************************************
*   name : 泛型
*   description: 實作泛型
*   author : Bryant
*****************************************************************************************************************/
public class Pair<T1, T2> {
T1 obj1;
T2 obj2;
Pair(T1 one, T2 two) {
obj1 = one;
obj2 = two;
 }

public T1 getFirst() {
      return obj1;
}

public T2 getSecond() {
      return obj2;
 }

public static void main(String[] args) {
Pair<Integer, Integer> pair = new Pair<Integer, Integer>(100, 100);
Integer sum = pair.getFirst() + pair.getSecond();
System.out.println("Sum:"+sum);

}
}

Ex :
哪幾種在編譯上或執行上不會出現error
1. List<Integer> intList = new List<Integer>();
2. List<Integer> intList = new ArrayList<Integer>();  (O)
3. List<Number> intList = new ArrayList<Integer>();
4. List<Integer> intList = new ArrayList<Number>();

(2)Diamond 鑽石
 1. List<Integer> intList = new ArrayList<Integer>(); 
 2. List<Integer> intList = new ArrayList<>();  //JAVA 7.0新增Diamond的寫法,
第一個和第二個是相同的,Diamond寫法編譯器會將左邊的型別自動去推算右邊的型別。

Ex: 下列宣告的效果都是一樣
     List<Integer> intList = new ArrayList<Integer>(); 
     List<Integer> intList = new ArrayList<>(); 
     List<Integer> intList = new ArrayList(); 


(3) 泛型的限制
1. 泛型不可以接受基本型別的宣告(int, double, float....)
2. Iterator 支援泛型結構
3. 使用<?>來完成萬用泛型, List<Object> 跟 List<?>不太一樣,List<?>是任何List的supertype 

泛型的資料型態Autoboxing/Unboxing
/*****************************************************************************************************************
*   name : 泛型範例
*   description:  Autoboxing 和 Unboxing
*   author : Bryant
*****************************************************************************************************************/
public static void main(String[] args) {
// TODO Auto-generated method stub
Vector<Integer> vec = new Vector() ;
vec.add(200) ; //自動封裝為Integer
vec.add(500) ; // 自動封裝為Integer

for( int i : vec){  // Unboxing
System.out.println(" number: "+i);
}
}