1. 程式人生 > >持久對象(編程思想)

持久對象(編程思想)

args ext dom 遍歷 err art 兩個 ack 新的

技術分享

持有對象
java中引用太多了,所以使用容器裝載。

泛型:
容器中裝載不同對象,取出時候強制類型轉換容易出錯
class Apple {
private static long counter;
private final long id = counter++;
public long id() { return id;}
}
class Orange {}
public class ApplesAndOrangesWithoutGenerics {
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for(int i = 0; i < 3; i++) {
apples.add(new Apple());
apples.add(new Orange());
}
for(int i = 0; i < apples.size(); i++){
((Apple)apples.get(i)).id();
}
}
}
應用預編譯得到泛型非常簡單,可以在編譯器防止將錯誤類型的對象防止到容器中。
public class ApplesAndOrangesWithGenerics {
public static void main(String[] args) {
ArrayList<Apple> apples = new ArrayList<Apple>();
for(int i = 0; i < 3; i++){
apples.add(new Apple());
// apples.add(new Orange()); 編譯報錯
}
for(Apple apple : apples){
System.out.println(apple.id());
}
}
}
當你指定了某個類型作為泛型參數時,你並不僅限於只能將該確切類型的對象放置到容器中。向上轉型也可以像作用於其他類型一樣作用於泛型。

基本概念
java容器類類庫的用途是“保存對象”,並將其劃分為兩個不同的概念:
Collection。一個獨立元素的序列。List按照插入的順序保存元素、Set不可重復,無序、Queue隊列
Map 一組成對的“鍵值對”對象,允許你使用鍵來查找值。映射表允許我們使用另一個對象來查找某個對象,它也被稱為“關聯數組”,因為它將某些對象與另外一些對相關聯在了一起;或者被稱為“字典”,因為你可以使用鍵對象來查找值對象,就像在字典中使用單詞來定義一樣。

Colction.add()
Collection.addAll()
Collections.addAll()
Arrays.asList();
如果使用Arrays.asList()的輸出,將其當做List,但是在這種情況下,其底層表示的是數組,因此不能調整尺寸。如果嘗試使用add()或者delete()在這種列表中添加或者刪除元素,就可能引發去改變數組尺寸的嘗試
class Snow {}
class Powder extends Snow {}
class Light extends Powder {}
class Heavy extends Powder {}
class Crusty extends Snow {}
class Slush extends Snow {}
public class AsListInference {
public static void main(String[] args) {
List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder());
// List<Snow> snow2 = Arrays.asList(new Light(), new Heavy());
List<Snow> snows3 = new ArrayList<Snow>();
Collections.addAll(snows3, new Light(), new Heavy());

List<Snow> snows4 = Arrays.<Snow>asList(new Light(), new Heavy());
}
}
可以像操作snows4的操作中所看到的,在Arrays.asList()中間插入一條"線索",以告訴編譯器對於Arrays.asList()產生的List類型,實際的目標類型應該是什麽。

List:
List承諾可以將元素維護在特定的序列中。List接口在Collection的基礎上添加了大量的方法,使得可以在List的中間插入和移除元素。
ArrayList:
LinkedList
constains()
remove()
indexOf()
equals()
優化是一個很棘手的問題,最好的策略就是置之不顧,指導你發現需要擔心它了。
subList()允許你從較大的列表中創建出一個片段,而將其結果傳遞給這個較大的列表的containsAll()方法時,自然會得到true。
Collections.sort()
Collections.shuffle()
retainAll()有效的交集操作,依賴於equals方法。
removeAll()
set(),在指定的索引處,用第二個參數替換整個位置的元素
isEmpty()
clear()

叠代器
對於List,add()是插入元素的方法之一,get()是取出元素的方法之一
使用容器,必須對容器的確切類型編程。考慮在下面的情況:如果原本是對著List編碼的,但是後來發現如果能夠把相同的代碼應用於Set,將會顯得非常方便,此時怎麽做?
叠代器的概念可以用於達成此目的,叠代器是一個對象,它的工作是遍歷並選擇序列中的對象,而客戶端程序員不必知道或關心序列底層的結構。此外,叠代器通常被稱為輕量級對象:創建它的代價小。
iterator()容器返回一個Iterator
next()獲得序列中的下一個元素
hasNext()間插序列中是否還有元素
remove()移除由next()產生的最後一個元素
ListIterator
只能用於各類List的訪問,ListIterator可以雙向移動
LinkedList添加了可以作用於棧、隊列或雙端隊列的方法
Stack可以內部包含一個LinkedList來完成模擬,util包裏面的Vector是集成Vector實現的。

Set不保存重復的元素。Set無需,這是因為HashSet使用散列,TreeSet將元素存儲在紅-黑樹數據結構中,HashSet使用的是散列函數,LinkedHashList因為查詢速度的原因也使用了散列,但是看起來它使用鏈表來維護元素的插入順序。
排序使用TreeSet
contains可以判斷數據是否處於容器內
Queue
先進先出的容器
offer()將一個元素插入到隊尾
peek()和element()在不移除的情況下返回隊頭,peek()在隊列為空時返回null,element()拋出NoSuchElementException異常
pool和remove()移除並返回隊頭。為空的情況下前者返回null,後者拋異常
PriorityQueue
典型的隊列規則是下一個元素應該是等待時間最長的元素。這個是優先級隊列
優先級隊列聲明下一個彈出元素是最需要的元素(具有最高的優先級)。
PriorityQueue上調用offer()方法來插入一個對象時,這個對象會在隊列中被排序。默認的排序將使用對象在隊列中的自然排序,但是你可以通過提供自己的Comparator來修改這個排序。

public class PriorityQueueDemo {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
Random rand = new Random(47);
for(int i = 0; i < 10; i++){
priorityQueue.offer(rand.nextInt(i + 10));
}
printQueue(priorityQueue);

List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
priorityQueue = new PriorityQueue<Integer>(ints);
printQueue(priorityQueue);

priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.<Integer>reverseOrder());
priorityQueue.addAll(ints);
printQueue(priorityQueue);

String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
List<String> strings = Arrays.asList(fact.split(" "));
PriorityQueue<String> stringsPQ = new PriorityQueue<String>(strings);
printQueue(stringsPQ);

stringsPQ = new PriorityQueue<String>(strings.size(), Collections.<String>reverseOrder());
stringsPQ.addAll(strings);
printQueue(stringsPQ);
}

public static <T> void printQueue(Queue<T> queue){
for(T obj : queue){
System.out.print(obj + " ");
}
System.out.println();
}
}
Foreach與叠代器
目前為止,foreach語法主要用於數組,但是它也可以應用於任何Collection對象。
public class ForeachCollections {
public static void main(String[] args) {
Collection<String> cs = new LinkedList<String>();
Collections.addAll(cs, "Take the long way home".split(" "));
for(String s : cs){
System.out.print("‘" + s + "‘ ");
}
}
}
Foreach之所以能工作,是因為Java SE5引入了新的被稱為Iterable的接口,該接口包含一個能夠產生Iterator的iterator()方法,並且Iterable接口被foreach用來在序列中移動。因此如果你創建了任何實現Iterator的類,都可以將它用於forEach語句中。
public class IterableClass implements Iterable<String>{
protected String[] words = "And that is how we know the Earth to be banana-shaped.".split(" ");

public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < words.length;
}

@Override
public String next() {
return words[index++];
}

public void remove() {
throw new UnsupportedOperationException();
}
};
}

public void forEach(Consumer<? super String> action) {

}

public Spliterator<String> spliterator() {
return null;
}

public static void main(String[] args) {
ArrayList al = new ArrayList();
for(String s : new IterableClass()){
System.out.println(s + " ");
}
}
}
iterator()方法返回的是實現了Iterator<String>的匿名內部類的實例,該匿名內部類可以遍歷數組中的所有單詞。在main中可以發現IterableClass確實可以用於foreach語句中。
foreach語句可以用於數組或者其他任何Iterable,但是這並不意味著數組肯定也是一個Iterable,而任何自動包裝也不會自動發生
public class ArrayIsNotIterable {
static <T> void test(Iterable<T> ib) {
for(T t : ib){
System.out.println(t + " ");
}
}
public static void main(String[] args) {
test(Arrays.asList(1, 2, 3));
String[] strings = {"A", "B", "C"};
// test(strings); 編譯報錯
test(Arrays.asList(strings));
}
}
適配器方法慣用法
如果現在又一個Iterable類,你想要添加一種或多種在foreach語句中使用這個類的方法,應該怎麽做呢?例如,假設你希望可以選擇以向前的方向或是向後的方向叠代一個單詞列表。如果直接繼承這個類,並覆蓋iterator()方法,你只能替換現有的方法,而不能實現選擇。
一種解決方案是所謂適配器方法的慣用法。“適配器”部分設計模式,因為你必須提供特定接口以滿足foreach語句。當你有一個接口並需要另一個接口時,編寫適配器就可以解決問題。
class ReversibleArrayList<T> extends ArrayList<T> {
public ReversibleArrayList(Collection<T> c) { super(c); }

public Iterable<T> reversed() {
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
int current = size() - 1 ;
public boolean hasNext(){ return current > -1; }
public T next() { return get(current--); }
public void remove() {
throw new UnsupportedOperationException();
}
};

}
};
}

}


public class AdapterMethodIdiom {
public static void main(String[] args) {
ReversibleArrayList<String> ral = new ReversibleArrayList<>(Arrays.asList("To be or not to be".split(" ")));
for(String s : ral){
System.out.print(s + " ");
}
System.out.println();
for (String s : ral.reversed()) {
System.out.print(s + " ");
}
}
}

持久對象(編程思想)