搞懂異地多活,看這篇就夠了【轉】
Map及實現類
Map介面是一個獨立的介面,和Collection介面沒有任何關係。
Map集合的特點: 每個元素不是一個物件,而是兩個物件。
鍵物件(key):不能重複
值物件(value):可以重複 Map集合中的一個鍵值對是一個整體,不能分開。
Map集合最常見的用法:通過鍵物件獲得值物件。
Map是一個介面,規定了實現類需要實現的方法:
Map介面的實現類:
HashMap:無序的鍵值對的集合,為Hashtable的替代品,比HashTable效率高
LinkedHashMap:使用連結串列實現的鍵值對的集合,鍵值對的順序和放入的順序一致
TreeMap:可以對鍵值對按照鍵物件排序的Map集合,鍵物件需要實現Comparable介面
Map介面的實現類:
Hashtable:雜湊表,效率低
Properties:鍵物件和值物件都是String型別的雜湊表,常用來讀寫配置檔案(一般的時候這麼用)
使用Map型別的引用,只關心共性,不關心每個實現類的細節。
Map的遍歷有三種方式:
鍵遍歷:取出所有的鍵物件放入一個Set中遍歷
值遍歷:取出所有的值物件放入一個Collection中遍歷
鍵值遍歷:把一個鍵值對視為一個物件,取出所有的鍵值對,放入一個Set中遍歷
鍵遍歷:
Map介面提供了keySet( )方法,可以把所有的鍵物件放入一個Set集合中:
Set set = map.keySet( );
遍歷這個Set集合就是遍歷Map的所有鍵物件。
值遍歷:
Map介面提供了values( )方法,可以把所有的鍵物件放入一個Collection集合中:
Collection c = map.values( );
遍歷這個Collection集合就是遍歷Map的所有值物件。
鍵值遍歷:
Map介面中有一個內部介面Map.Entry,這個介面的實現類封裝了一個鍵值對,並且提供了獲取鍵物件和值物件的方法:
getKey( ):獲取鍵物件
getValue( ):獲取值物件
鍵值遍歷:
Map介面提供了entrySet( )方法,可以把所有的Entry物件放入一個Set集合中:
Set set = map.entrySet( );
遍歷這個Set集合就是遍歷Map的所有鍵值對。
鍵值遍歷: 遍歷Set集合中的Entry物件,在從Entry物件中取出鍵物件和值物件進行遍歷:
Iterator it = set.iterator( ); while( it.hasNext( ) ) { Map.Entry entry = ( Map.Entry )it.next( ); System.out.println( entry.getKey( ) ); System.out.println( entry.getValue( ) ); }
增強的for遍歷
集合是用來代替陣列的。
如果一個專案中使用陣列實現的部分,改用Set集合實現,那麼所有遍歷陣列的程式碼都要重新寫,這很有可能是一個非常大的工作量。
能不能使用一個通用的遍歷方式,能夠同時遍歷陣列、List集合和Set集合呢?
Java提供了一種增強的for遍歷方式,也叫for-each遍歷:
for ( 元素型別 變數 : 陣列/List集合/Set集合 ) {
// 使用變數訪問每一個遍歷到的元素
}
雖然寫法和for迴圈非常相似,但是for遍歷和for迴圈是兩種不同的語句。
遍歷並輸出陣列中的元素:
Student[ ] array = … // 建立陣列 for ( Student s : array ) { System.out.println( s ); }
遍歷並輸出List集合中的元素:
List list = new ArrayList(); …… // 向list中增加元素 for ( Object o : list ) { System.out.println( o ); }
遍歷並輸出Set集合中的元素:
Set set = new HashSet(); …… // 向set中增加元素 for ( Object o : set ) { System.out.println( o ); }
for遍歷可以遍歷什麼?
陣列
Iterable介面的實現類的物件
思考:如何使用for遍歷訪問Map集合中的元素?
把Map集合轉為Set集合在遍歷
二叉樹
為什麼TreeSet和TreeMap可以給物件排序?
因為TreeSet和TreeMap都是使用二叉樹實現的,而二叉樹可以給物件排序。
二叉樹是一種資料結構,和連結串列一樣,二叉樹也是使用節點組成的。
二叉樹的每個節點包含三個部分:
資料域(data):儲存一個元素
指標域(lChild):儲存左子節點的記憶體地址
指標域(rChild):儲存右子節點的記憶體地址
class Tree { private int data; private Tree lChild; private Tree rChild; public Tree( Object data ) { this.data = data; } ……//getter & setter }
思考:如果只有一個二叉樹根節點的引用,如何獲得二叉樹擁有的節點數量?
分析:一個二叉樹的所有節點數量為:
左子樹節點數量 + 右子樹節點數量 + 1
public int getNodes( ) { return ( this.lChild == null ? 0 ://左節點數 this.lChild.getNodes( ) ) + ( this.rChild == null //右節點數 ? 0 : this.rChild.getNodes( ) ) + 1;//加上自身節點數 }
思考:如果只有一個二叉樹根節點的引用,如何獲得二叉樹所有節點儲存陣列的總和?
public int sum( ) { return ( this.lChild == null ? 0 : this.lChild.sum( ) ) + ( this.rChild == null ? 0 : this.rChild.sum( ) ) + this.data; }
思考:如何遍歷一個二叉樹中的所有節點?
二叉樹的遍歷有三種方式:
先序遍歷
中序遍歷
後序遍歷
先序遍歷的步驟:
訪問根節點
先序遍歷左子樹
先序遍歷右子樹
public void fTraverse( ) { System.out.println( this.data ); if( this.lChild != null ) { this.lChild.fTraverse( ); } if( this.rChild != null ) { this.rChild.fTraverse( ); } }
中序遍歷的步驟:
中序遍歷左子樹
訪問根節點
中序遍歷右子樹
public void mTraverse( ) { if( this.lChild != null ) { this.lChild.mTraverse( ); } System.out.println( this.data ); } if( this.rChild != null ) { this.rChild.mTraverse( ); }
後序遍歷的步驟:
後序遍歷左子樹
後序遍歷右子樹
訪問根節點
public void lTraverse( ) { if( this.lChild != null ) { this.lChild.lTraverse( ); } if( this.rChild != null ) { this.rChild.lTraverse( ); } System.out.println( this.data ); }
思考:
根據一個int型別的陣列建立一個二叉樹,要求:
陣列中的第一個元素為二叉樹的根節點 二叉樹和它的所有子樹還要滿足以下要求:
二叉樹如果有左子樹,左子樹的所有節點元素必須小於二叉樹的根節點元素
二叉樹如果有右子樹,右子樹的所有節點元素必須大於或等於二叉樹的根節點元素
如何實現以上演算法?
步驟: 以第一個元素為根,後面元素依次和根元素比較
如果小於根元素
根元素沒有左子節點,則作為根元素的左子節點 根元素有左子節點,則繼續遞迴的和左子節點比較
否則 根元素沒有右子節點,則作為根元素的右子節點
根元素有右子節點,則繼續遞迴的和右子節點比較
public void addNode( Tree node ) { if ( node.data < this.data ) { if ( this.lChild == null ) { this.lChild = node; } else { this.lChild.addNode( node ); } } …… }
public void addNode( Tree node ) { …… else { if ( this.rChild == null ) { this.rChild = node; } else { this.rChild.addNode( node ); } } }
在操作二叉樹時,遞迴的思想無處不在。
只要是樹形結構,就離不開遞迴演算法。
在無法使用迴圈的地方,遞迴往往能夠發揮奇效。