1.調用泛型方法
public static void main(String[] args) {
String[] s1 = {"1", "3", "5"};
test.<String>print(s1);
}
public static <T> void print(T[] data) {
for (int i = 0; i < data.length; i++) {
System.out.println(data[i]);
}
}
使用.<類型>函數方法
的方式調用泛型方法,而此<類型>
是可以省略的。
2.泛型類型的定義
- 定義類為泛型類型,需要將泛型類型放在類的后面 如
public class test<T>
- 定義方法為泛型類型,需要將泛型類型寫在方法返回類型的前面。如
public static <T> void print(T[] data)
3.泛型通配符
為了避免上面的問題,我們使用通配符來解決。
通配符的三種形式
- ? 非限制通配符,和
? extends object
一樣- ? extends T 上限通配,也就是必須是T的子類型(包含T)
- ? supter T 下限通配,也就是必須是T的父類型(包含T)
4.類型擦除
java
中的泛型使用的是類型擦除的方法實現的。泛型只存在于編譯階段,而在運行階段是不存在泛型類型的,一旦編譯器在編譯時期檢測類型是安全的,就會將它轉換為原始類型。
下面是編譯完成后的.class
運行時文件:
也就是說在運行時是沒有泛型這一概念的,泛型只在編譯期用于檢測類型安全。
為什么new E()是錯誤的?
在java
中,不能直接創建泛型的對象,因為創建對象是在運行時動態創建的,而運行時是沒有泛型類型的。
5.泛型的幾種常見的限制
- 不能使用
new E()
,也就是不能創建泛型對象- 不能使用
new E[]
,也就是不能創建泛型數組- 靜態方法不能使用類的泛型類型作用參數
- 異常類不能是泛型的
6.Collections
1.Collections.nCopies
使用此方法可以創建指定對象的n個副本的不可變list。此方法接收一個對象,并接收一個int count
的參數來指定要創建幾個該對象的副本。也就是說,此list
中會有n個該對象的副本,而且此list
是不可變的,也就是不進對該list
進行修改的操作。否則,會報錯:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at generic.test.main(test.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
2.Collections.fill
表示用指定元素填充(覆蓋)原集合中的所有元素。
List<Integer> mDatas = new ArrayList<>();
mDatas.add(1);
mDatas.add(2);
mDatas.add(3);
Collections.fill(mDatas, 4);
打印結果為4 4 4
3.使用Collections
建立單元素和不可變的集合和map
7.Map類型及作用
常用的Map
類有hashMap
、linkedHashMap
、TreeMap
。
- 如果更新map時不需要保持元素的順序,就用HashMap
- 如果需要按插入時間或最后訪問時間來排序,就用LinkedHashMap
- 如果需要按鍵來進行排序,就使用TreeMap
TreeMap例子
對一段字符串進行分割,然后統計每個被分割部分的單詞出現的次數,并按字母的自然順序進行排序。
8.算法時間復雜度的計算
在計算算法的時間復雜度時,我們主要看算法時間的增加,主要是由哪個變量引起的,就取此值,寫法為O(值)
。比如:
下面是關于指數級的時間復雜度的計算:
各時間復雜度的對比關系:
9.優化的求最大公約數的算法
private static int getMaxGCD(int m, int n) {
int gcd = 1;
if (m % n == 0) {
return n;
}
for (int i = n / 2; i >= 1; i--) {
if (m % i == 0 && n % i == 0) {
gcd = i;
break;
}
}
return gcd;
}
最優化的方法:
private static int getMaxGCD(int m, int n) {
if (m % n == 0) {
return n;
}
return getMaxGCD(n, m % n);
}
10.StringTokenizer的用法
StringTokenizer
的作用主要是根據指定的分割符將字符串分割成n部分,然后通過hasMoreToken
來判斷是否還有下一個被分割的部分,nextToken
的方法來獲取對應的數據。
StringTokenizer stringTokenizer = new StringTokenizer("www.baidu.com", ".a", true);
while (stringTokenizer.hasMoreTokens()) {
System.out.println("token:" + stringTokenizer.nextToken());
}
上面的參數二,表示按.
或者a
來對字符串進行分割,也就是遇到.
或者a
就進行分割,默認本身不被算作分割的部分。
參數三,表示將本身也算作分割的一部分。
上面的結果為:
token:www
token:.
token:b
token:a
token:idu
token:.
token:com
11.二叉查找樹
二叉查找樹的特征:對于樹中的每一個節點,它的左子樹中的節點都是小于當前節點的值的;而它的右子樹中的節點都是大于當前節點的值的。
判斷是否包含某個元素
在二叉樹中添加一個元素
在二叉樹中添加一個元素,有兩種情況:
- 二叉樹中已經存在此元素,會直接丟棄要插入的元素
- 將元素添加到某個葉子節點的下一層,也就是最底層。
從上面的代碼示例子,可以得知,當current
為null,也就是while
循環結束后,就是添加元素所要插入的位置了。通過此元素創建新節點,然后與parent
的值進行比較,大于就添加到右邊,小于就添加到左邊。
中序遍歷
遞歸地先遍歷左子樹,然后訪問當前節點,最后右子樹。中序遍歷法以遞增順序顯示BST
中的所有節點。
后序遍歷
首先遞歸地遍歷左子樹,然后遞歸地遍歷右子樹,最后訪問該節點本身。
前序遍歷
首先遞歸訪問當前節點,然后遞歸地訪問該節點的左子樹,最后遞歸地訪問該節點的右子樹。
廣度優先遍歷
逐層訪問樹中的節點。首先訪問根節點,然后從左往右訪問根節點的孩子節點,然后再從左住右的訪問根節點的所有孫子節點,以此類推。
廣度優先遍歷主要是從層級關系出發,一層一層的對樹進行遍歷。
12.圖
1.圖的說明
圖由頂點和邊組成,分為有向圖和無向圖。
2.圖的表示
圖一般由兩種常用的方式來表示,一種是鄰接矩陣(也就是二維數組);另一種是鄰接線性表(鏈表).一般根據圖的情況(邊的多少),來決定使用哪種表示方式:
- 如果邊比較多,一般使用鄰接矩陣。
- 如果邊比較少,一般使用鄰接線性表。
- 由于鄰接矩陣會占用大量的存儲空間,一般建議使用鄰接純線性表。
3.圖的遍歷
- 深度優先搜索
- 廣度優先搜索
13.多線程
1.新建的線程什么時候會被關閉
當run
方法中的代碼都執行完畢后,就會被終止。
2.直接調用run
方法和start
的區別
直接調用run
方法,任務會在當前線程上運行,而不是新創建一個線程。
3.使用Runnable
,而不直接使用Thread
來創建并執行任務的好處
使用Runnable
的好處就是把任務和運行任務進行了解耦,更方便復用。
4.yield
讓cpu為其它線程臨時讓出cpu
執行時間,并且只能讓同優先級及以上線程有執行機會。
5.sleep
暫停執行一段時間,以給其它線程運行機會,不會考慮線程的優先級。會拋出interruptedException
異常,如果線程在sleep
狀態下被調用interrupt
方法(也就是終止),就會拋出此異常。
在synchronized
代碼執行中,不會釋放對象鎖。
6.sleep和yield的區別
- sleep方法給其它線程運行機會時不會考慮線程的優先級,而
yield
只會給同級別或者更高級別以運行機會。- 線程執行
sleep
方法后會轉入阻塞
狀態,而調用yield
方法后,會進入就緒狀態。sleep
方法聲明拋出InterruptedException
異常,而yield
沒有聲明任何異常sleep
方法比yield
方法有更好的可移植性。(在操作系統調度方面)
7.wait
讓線程讓出cpu
執行機會,一旦調用了wait
方法后,只能通過notify
或者notifyAll
來喚醒該線程。也會拋出InterruptedException
異常。調用wait
后線程處于掛起狀態。
如果某個線程獲得了對象鎖,在調用了wait
方法后,會釋放該對象鎖。
同時調用wait
之前,該線程必須要獲得線程鎖,否則會拋出IllegalMonitorStateException
異常。
8.wait和sleep的區別
wait
只能在同步方法或者同步代碼塊中被調用,因為需要獲取對象鎖,否則會拋出IllegalMonitorStateException
異常,而sleep
可以在任何地方被調用。wait
屬于object
的方法,而sleep
是Thread
的方法- 在同步代碼中執行時,
sleep
不會釋放線程鎖,而wait
會釋放線程鎖。- 執行
wait
后,線程進入掛起狀態,只能通過notify
或者notifyAll
才能被喚醒,而sleep
被調用后,會進入阻塞狀態,睡眠時間一過,就會被自動喚醒。
9.join
join
的作用是:使一個線程等待另一個線程的結果后再開始執行。
10.setPriority
使用該方法可以設置線程的優先級。
11:線程池
Executor
是java
中的一個線程池類。一般我們通過靜態方法來創建線程池。也可以通過ThreadPoolExecutor
類來創建一個自定義的線程池。
- Executors.newFixedThreadPool(int i)
創建固定線程數量的線程池,處于線程池中的線程不會被關閉,除非調用interrupt
方法將其終止。
2.Executors.newCachedThreadPool()
此線程池為緩存可伸縮的非常靈活的線程池。當有大量任務需要執行時,該線程池會為任務創建新的線程,可創建線程的數量取決于JVM
可創建的最大線程大小。
而當任務執行完畢后,線程處于空閑狀態后60s
,會將此線程進行回收。
3.Executors.newSingleThreadExecutor()
創建一個單線程的線程池,這個線程池只有一個線程在工作,也就是相當于單線程串行執行所有任務。
4.Executors.newScheduledThreadPool()
創建一個大小無限的線程池,此線程池支持定時或周期性執行任務的需求。
//執行線程池大小
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
long initialDelay1 = 1;
long period1 = 1;
// 從現在開始1秒鐘之后,每隔1秒鐘執行一次job1
service.scheduleAtFixedRate(
new ScheduledExecutorTest("job1"), initialDelay1,
period1, TimeUnit.SECONDS);
5.Executor.shutdown和shutdown的區別
shutdown調用后,不可以再submit新的task,已經submit的將繼續執行。
shutdownNow試圖停止當前正執行的task,并返回尚未執行的task的list
6.不帶緩沖區的生產者/消費者的示例
1.depositTask代碼(存錢的任務)
public class DepositTask implements Runnable {
private Acount mAcount;
public DepositTask(Acount acount) {
this.mAcount = acount;
}
@Override
public void run() {
while (true) {
try {
mAcount.deposit((int) (Math.random() * 10 + 1));
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.WithDrawTask(取錢的任務)
public class WithDrawTask implements Runnable {
private Acount mAcount;
public WithDrawTask(Acount acount) {
this.mAcount = acount;
}
@Override
public void run() {
mAcount.withDraw((int) (Math.random() * 10 + 1));
}
}
3.Accout(帳戶)
public class Acount {
private int balance;
private static Lock lock = new ReentrantLock();
private static Condition mCondition = lock.newCondition();
public void deposit(int amount) {
lock.lock();
try {
balance += amount;
System.out.println("deposit:" + balance);
mCondition.signalAll();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
public void withDraw(int amount) {
lock.lock();
try {
while (amount > balance) {
System.out.println("withDraw: wait.....");
mCondition.await();
}
balance -= amount;
System.out.println("withdraw:" + amount + "======balance:" + balance);
} catch (Exception e) {
} finally {
lock.unlock();
}
}
}
4.測試類
public class test {
private static Acount acount = new Acount();
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new DepositTask(acount));
service.execute(new WithDrawTask(acount));
service.shutdown();
}
}
12。 concurrent包
1.BlockingQueue
BlockingQueue
(阻塞隊列):是一個依賴生產者/消費者模型來設計的,主要通過put
(添加)和take
(取出)來模擬生產者和消費者。事實上,使用其它方法添加和刪除時,當隊列為空或者已滿的情況下,是會拋出異常的。
ArrayBlockingQueue(數組阻塞隊列)
此隊列是一個FIFO的有界隊列,有界是說明它的大小是有限的,必須給其設置一個值。
DelayQueue(延遲隊列)
此隊列主要是延遲處理隊列中的元素,可以為中的元素設置一個過期時間,當使用take
去取數據時,如果過了過期時間,可以馬上取出數據,如果還沒到過期時間,則等待到達過期時間后才能取出該數據。
在使用此隊列時,需要添加的元素實現Delayd
接口。
class DelayedElement implements Delayed {
private final long delay;
private final long expire;
public DelayedElement(long delay) {
this.delay = delay;
expire = Calendar.getInstance().getTimeInMillis() + delay;
}
@Override
public long getDelay(TimeUnit unit) {
return expire - Calendar.getInstance().getTimeInMillis();
}
@Override
public int compareTo(Delayed o) {
return (int) (this.delay - o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return "DelayedElement is " + delay + "\n" + Calendar.getInstance().getTime().toString() + "\n elapsed time is " + getDelay(TimeUnit.DAYS);
}
}
這樣子看起來,好像這個對象并沒有做什么其它的操作,只是在判斷當前對象是否超時,而沒有具體的東西。你可以在構造方法中加入具體的業務對象來與此延遲對象來進行綁定。
LinkedBlockingQueue(鏈式阻塞隊列)
LinkedBlockingQueue
是一個FIFO鏈式結構的隊列,可以指定隊列的最大值,默認為Integer.maxValue()
.
PriorityBlockingQueue(優先級阻塞隊列)
PriorityBlockingQueue(優先級隊列)
是一個無界的并發隊列。添加到隊列中的元素必須實現Comparable
接口(讓其進行排序)
SynchronousQueue(同步隊列)
SynchronousQueue(同步隊列)
:隊列中只能有一個元素,當存在一個元素后,添加操作會進入阻塞,此時的操作應該是取出此單個元素,取出元素后,取出的操作就會被阻塞,而等待添加新的元素,依此循環。
blockingDeque(阻塞雙端隊列接口)
blockingDeque(阻塞雙端隊列)
:表示可以從任意一端插入數據或者取出數據。當隊列滿時,插入操作會進入阻塞,而當隊列為空時,取出數據會進入阻塞狀態。通過addFirst/addLast
以及removeFirst/removeLast
.此接口的唯一實現類為LinkedBlockingDeque
LinkedBlockingDeque(鏈式阻塞雙端隊列)
由鏈表組成的雙端可讀寫的阻塞式隊列。
2.信號量
信號量可以用來訪問共享資源的線程數,線程必須從信號量中獲取許可,訪問完資源后,這個線程需要把許可返回給信號量。