Java基礎(7)
文章目錄
Java集合、泛型和列舉
Java 8中Map新增的方法
Java 8 除了為 Map 增加了 remove(Object key, Object value) 預設方法之外,還增加了如下方法
名稱 | 說明 |
---|---|
Object compute(Object key, BiFunction remappingFunction) | 該方法使用 remappingFunction 根據原 key-value 對計算一個新 value。只要新 value 不為 null,就使用新 value 覆蓋原 value;如果原 value 不為 null,但新 value 為 null,則刪除原 key-value 對;如果原 value、新 value 同時為 null,那麼該方法不改變任何 key-value 對,直接返回 null。 |
Object computeIfAbsent(Object key, Function mappingFunction) | 如果傳給該方法的 key 引數在 Map 中對應的 value 為 null,則使用 mappingFunction 根據 key 計算一個新的結果,如果計算結果不為 null,則用計算結果覆蓋原有的 value。如果原 Map 原來不包括該 key,那麼該方法可能會新增一組 key-value 對。 |
Object computeIfPresent(Object key, BiFunction remappingFunction) | 如果傳給該方法的 key 引數在 Map 中對應的 value 不為 null,該方法將使用 remappingFunction 根據原 key、value 計算一個新的結果,如果計算結果不為 null,則使用該結果覆蓋原來的 value;如果計算結果為 null,則刪除原 key-value 對。 |
void forEach(BiConsumer action) | 該方法是 Java 8 為 Map 新增的一個遍歷 key-value 對的方法,通過該方法可以更簡潔地遍歷 Map 的 key-value 對。 |
Object getOrDefault(Object key, V defaultValue) | 獲取指定 key 對應的 value。如果該 key 不存在,則返回 defaultValue。 |
Object merge(Object key, Object value, BiFunction remappingFunction) | 該方法會先根據 key 引數獲取該 Map 中對應的 value。如果獲取的 value 為 null,則直接用傳入的 value 覆蓋原有的 value(在這種情況下,可能要新增一組 key-value 對);如果獲取的 value 不為 null,則使用 remappingFunction 函式根據原 value、新 value 計算一個新的結果,並用得到的結果去覆蓋原有的 value。 |
Object putIfAbsent(Object key, Object value) | 該方法會自動檢測指定 key 對應的 value 是否為 null,如果該 key 對應的 value 為 null,該方法將會用新 value 代替原來的 null 值。 |
Object replace(Object key, Object value) | 將 Map 中指定 key 對應的 value 替換成新 value。與傳統 put() 方法不同的是,該方法不可能新增新的 key-value 對。如果嘗試替換的 key 在原 Map 中不存在,該方法不會新增 key-value 對,而是返回 null。 |
boolean replace(K key, V oldValue, V newValue) | 將 Map 中指定 key-value 對的原 value 替換成新 value。如果在 Map 中找到指定的 key-value 對,則執行替換並返回 true,否則返回 false。 |
replaceAll(BiFunction function) | 該方法使用 BiFunction 對原 key-value 對執行計算,並將計算結果作為該 key-value 對的 value 值。 |
下面程式示範了 Map 常用預設方法的功能和用法
import java.util.Map;
import java.util.HashMap;
public class Test13 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Map map = new HashMap();
// 成對放入多個key-value對
map.put("Java入門教程", 10);
map.put("C語言入門教程", 20);
map.put("Python基礎教程", 30);
// 嘗試替換key為”Go語言入門教程”的 value,由於原 Map 中沒有對應的 key
// 因此Map沒有改變,不會新增新的key-value對
map.replace("Go語言入門教程", 40);
System.out.println(map);
// 使用原value與傳入引數計算出來的結果覆蓋原有的value,oldVal此時為原來的20,param為傳入的25
map.merge("C語言入門教程", 25, (oldVal, param) -> (Integer) oldVal + (Integer) param);
System.out.println(map);
map.merge("C語言入門教程2", 35, (oldVal, param) -> (Integer) oldVal + (Integer) param);
System.out.println(map);
// 當key為"Java"對應的value為null (或不存在)時,使用計算的結果作為新value
map.computeIfAbsent("Java", (key) -> ((String) key).length());
System.out.println(map); // map 中添加了 Java=4 這組 key-value 對
// 當key為"Java"對應的value存在時,使用計算的結果作為新value
map.computeIfPresent("Java", (key, value) -> (Integer) value * (Integer) value);
System.out.println(map); // map 中 Java=4 變成 Java=16
}
}
Java Collections類操作集合詳解
Collections 類是 Java 提供的一個操作 Set、List 和 Map 等集合的工具類。Collections 類提供了許多操作集合的靜態方法,藉助這些靜態方法可以實現集合元素的排序、查詢替換和複製等操作
排序(正向和逆向)
Collections 提供瞭如下方法用於對 List 集合元素進行排序。
- void reverse(List list):對指定 List 集合元素進行逆向排序。
- void shuffle(List list):對 List 集合元素進行隨機排序(shuffle 方法模擬了“洗牌”動作)。
- void sort(List list):根據元素的自然順序對指定 List 集合的元素按升序進行排序。
- void sort(List list, Comparator c):根據指定 Comparator 產生的順序對 List 集合元素進行排序。
- void swap(List list, int i, int j):將指定 List 集合中的 i 處元素和 j 處元素進行交換。
- void rotate(List list, int distance):當 distance 為正數時,將 list集合的後distance 個元素“整體”移到前面;當 distance 為負數時,將 list 集合的前 distance 個元素“整體”移到後面。該方法不會改變集合的長度。
對陣列進行排序後輸出
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class Test13 {
public void test2() {
List prices = new ArrayList();
prices.add(11);
prices.add(2);
prices.add(14);
prices.add(8);
Collections.sort(prices);
System.out.println(prices); // [2, 8, 11, 14]
Collections.reverse(prices);
System.out.println(prices); // [2, 8, 11, 14]
}
public static void main(String[] args) {
new Test13().test2();
}
}
查詢、替換操作
Collections 還提供瞭如下常用的用於查詢、替換集合元素的方法。
- int binarySearch(List list, Object key):使用二分搜尋法搜尋指定的 List 集合,以獲得指定物件在 List 集合中的索引。如果要使該方法可以正常工作,則必須保證 List 中的元素已經處於有序狀態。
- Object max(Collection coll):根據元素的自然順序,返回給定集合中的最大元素。
- Object max(Collection coll, Comparator comp):根據 Comparator 指定的順序,返回給定集合中的最大元素。
- Object min(Collection coll):根據元素的自然順序,返回給定集合中的最小元素。
- Object min(Collection coll, Comparator comp):根據 Comparator 指定的順序,返回給定集合中的最小元素。
- void fill(List list, Object obj):使用指定元素 obj 替換指定 List 集合中的所有元素。
- int frequency(Collection c, Object o):返回指定集合中指定元素的出現次數。
- int indexOfSubList(List source, List target):返回子 List 物件在父 List 物件中第一次出現的位置索引;如果父 List 中沒有出現這樣的子 List,則返回 -1。
- int lastIndexOfSubList(List source, List target):返回子 List 物件在父 List 物件中最後一次出現的位置索引;如果父 List 中沒有岀現這樣的子 List,則返回 -1。
- boolean replaceAll(List list, Object oldVal, Object newVal):使用一個新值 newVal 替換 List 物件的所有舊值 oldVal。
對陣列使用 Collections 類中的 fill() 方法對商品資訊進行重置操作
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class Test13 {
public void test3() {
List list = new ArrayList();
list.add("zengraoli1");
list.add("zengraoli2");
list.add("zengraoli3");
list.add("zengraoli4");
System.out.println(list);
Collections.fill(list, "zeng");
System.out.println(list);
}
public static void main(String[] args) {
new Test13().test3();
}
}
在一個集合中儲存 4 個數據,分別輸出最大最小元素和指定資料在集合中出現的次數。
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class Test13 {
public void test4() {
ArrayList nums = new ArrayList();
nums.add(2);
nums.add(-5);
nums.add(3);
nums.add(0);
nums.add(-5);
// 判斷-5在List集合中出現的次數,返回1
System.out.println(Collections.frequency(nums, -5));
Collections.sort(nums); // 對 nums集合排序
System.out.println(nums); // 輸出:[-5, 1, 2, 3]
// 只有排序後的List集合才可用二分法查詢,輸出3
System.out.println(Collections.binarySearch(nums, 3));
}
public static void main(String[] args) {
new Test13().test4();
}
}
複製
Collections 類的 copy() 靜態方法用於將指定集合中的所有元素複製到另一個集合中。執行 copy() 方法後,目標集合中每個已複製元素的索引將等同於源集合中該元素的索引。
copy() 方法的語法格式如下,其中,dest 表示目標集合物件,src 表示源集合物件
void copy(List <? super T> dest,List<? extends T> src)
Java使用Lambda表示式遍歷Collection集合
Java 8 為 Iterable 介面新增了一個 forEach(Consumer action) 預設方法,該方法所需引數的型別是一個函式式介面,而 Iterable 介面是 Collection 介面的父介面,因此 Collection 集合也可直接呼叫該方法。
當程式呼叫 Iterable 的 forEach(Consumer action) 遍歷集合元素時,程式會依次將集合元素傳給 Consumer 的 accept(T t) 方法(該介面中唯一的抽象方法)。正因為 Consumer 是函式式介面,因此可以使用 Lambda 表示式來遍歷集合元素
如下程式示範了使用 Lambda 表示式來遍歷集合元素
import java.util.List;
import java.util.ArrayList;
public class Test14 {
public static void main(String[] args) {
// TODO Auto-generated method stub
List list = new ArrayList();
list.add("zengraoli1");
list.add("zengraoli2");
list.add("zengraoli3");
list.forEach(obj -> System.out.println("迭代集合元素:" + obj));
}
}
上面程式中呼叫了 Iterable 的 forEach() 預設方法來遍歷集合元素,傳給該方法的引數是一個 Lambda 表示式,該 Lambda 表示式的目標型別是 Comsumer。forEach() 方法會自動將集合元素逐個地傳給 Lambda 表示式的形參,這樣 Lambda 表示式的程式碼體即可遍歷到集合元素了
Java Iterator(迭代器)遍歷Collection集合元素
Iterator 介面隱藏了各種 Collection 實現類的底層細節,嚮應用程式提供了遍歷 Collection 集合元素的統一程式設計介面。Iterator 接口裡定義瞭如下 4 個方法。
- boolean hasNext():如果被迭代的集合元素還沒有被遍歷完,則返回 true。
- Object next():返回集合裡的下一個元素。
- void remove():刪除集合裡上一次 next 方法返回的元素。
- void forEachRemaining(Consumer action):這是 Java 8 為 Iterator 新增的預設方法,該方法可使用 Lambda 表示式來遍歷集合元素
下面是使用forEachRemaining的程式碼,即Java Lambda表示式遍歷迭代器
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Test14 {
public void test2() {
Collection objs = new HashSet();
objs.add("zengraoli1");
objs.add("zengraoli2");
objs.add("zengraoli3");
Iterator it = objs.iterator();
it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj));
}
public static void main(String[] args) {
new Test14().test2();
}
}
Java foreach遍歷Collection集合
輸出具有不確定性
public void test3() {
Collection objs = new HashSet();
objs.add("zengraoli1");
objs.add("zengraoli2");
objs.add("zengraoli3");
for(Object value:objs) {
System.out.println("迭代集合元素:" + value);
}
}
Predicate操作Collection集合
Java 8 起為 Collection 集合新增了一個 removeIf(Predicate filter) 方法,該方法將會批量刪除符合 filter 條件的所有元素。該方法需要一個 Predicate 物件作為引數,Predicate 也是函式式介面,因此可使用 Lambda 表示式作為引數
如下程式示範了使用 Predicate 來過濾集合,刪掉等於zengraoli3的集合元素
import java.util.Collection;
public class Test14 {
public void test4() {
Collection objs = new HashSet();
objs.add("zengraoli1-1");
objs.add("zengraoli1-2");
objs.add("zengraoli3");
objs.removeIf(els -> els.equals("zengraoli3"));
System.out.println(objs);
}
public static void main(String[] args) {
new Test14().test4();
}
}
另外一個例子
public class ForeachTest2 {
public static void main(String[] args) {
// 建立一個集合
Collection objs = new HashSet();
objs.add(new String("C語言中文網Java教程"));
objs.add(new String("C語言中文網C++教程"));
objs.add(new String("C語言中文網C語言教程"));
objs.add(new String("C語言中文網Python教程"));
objs.add(new String("C語言中文網Go教程"));
// 統計集合中出現“C語言中文網”字串的數量
System.out.println(calAll(objs, ele -> ((String) ele).contains("C語言中文網")));
// 統計集合中出現“Java”字串的數量
System.out.println(calAll(objs, ele -> ((String) ele).contains("Java")));
// 統計集合中出現字串長度大於 12 的數量
System.out.println(calAll(objs, ele -> ((String) ele).length() > 12));
}
public static int calAll(Collection books, Predicate p) {
int total = 0;
for (Object obj : books) {
// 使用Predicate的test()方法判斷該物件是否滿足Predicate指定的條件
if (p.test(obj)) {
total++;
}
}
return total;
}
}
上面程式先定義了一個 calAll() 方法,它使用 Predicate 判斷每個集合元素是否符合特定條件,條件將通過 Predicate 引數動態傳入。從上面程式中第 11、13、15 行程式碼可以看到,程式傳入了 3 個 Lambda 表示式,其目標型別都是 Predicate,這樣 calAll() 方法就只會統計滿足 Predicate 條件的圖書
使用Java 8新增的Stream操作Collection集合
Java 8 還新增了 Stream、IntStream、LongStream、DoubleStream 等流式 API,這些 API 代表多個支援序列和並行聚集操作的元素。上面 4 個介面中,Stream 是一個通用的流介面,而 IntStream、LongStream、 DoubleStream 則代表元素型別為 int、long、double 的流。
Java 8 還為上面每個流式 API 提供了對應的 Builder,例如 Stream.Builder、IntStream.Builder、LongStream.Builder、DoubleStream.Builder,開發者可以通過這些 Builder 來建立對應的流。
獨立使用 Stream 的步驟如下:
- 使用 Stream 或 XxxStream 的 builder() 類方法建立該 Stream 對應的 Builder。
- 重複呼叫 Builder 的 add() 方法向該流中新增多個元素。
- 呼叫 Builder 的 build() 方法獲取對應的 Stream。
- 呼叫 Stream 的聚集方法。
示例程式碼如下
import java.util.stream.IntStream;
public class Test15 {
public void test1() {
IntStream is = IntStream.builder().add(20).add(13).add(-2).add(18).build();
// 下面呼叫聚集方法的程式碼每次只能執行一行
// System.out.println("is 所有元素的最大值:" + is.max().getAsInt());
// System.out.println("is 所有元素的最小值:" + is.min().getAsInt());
// System.out.println("is 所有元素的總和:" + is.sum());
// System.out.println("is 所有元素的總數:" + is.count());
// System.out.println("is 所有元素的平均值:" + is.average());
// System.out.println("is所有元素的平方是否都大於20: " + is.allMatch(ele -> ele * ele > 20));
// System.out.println("is是否包含任何元素的平方大於20 : " + is.anyMatch(ele -> ele * ele > 20));
// 將is對映成一個新Stream,新Stream的每個元素是原Stream元素的2倍+1
IntStream newIs = is.map(ele -> ele * 2 + 1);
newIs.forEach(p -> System.out.println(p));
// 等價於下面的寫法
newIs.forEach(System.out::println);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new Test15().test1();
}
}
上面程式先建立了一個 IntStream,接下來分別多次呼叫 IntStream 的聚集方法執行操作,這樣即可獲取該流的相關資訊。注意:上面 5~13 行程式碼每次只能執行一行,因此需要把其他程式碼註釋掉
Stream 提供了大量的方法進行聚集操作,這些方法既可以是“中間的”(intermediate),也可以是 “末端的”(terminal)。
- 中間方法:中間操作允許流保持開啟狀態,並允許直接呼叫後續方法。上面程式中的 map() 方法就是中間方法。中間方法的返回值是另外一個流。
- 末端方法:末端方法是對流的最終操作。當對某個 Stream 執行末端方法後,該流將會被“消耗”且不再可用。上面程式中的 sum()、count()、average() 等方法都是末端方法。
下面簡單介紹一下 Stream 常用的中間方法
方法 | 說明 |
---|---|
filter(Predicate predicate) | 過濾 Stream 中所有不符合 predicate 的元素 |
mapToXxx(ToXxxFunction mapper) | 使用 ToXxxFunction 對流中的元素執行一對一的轉換,該方法返回的新流中包含了 ToXxxFunction 轉換生成的所有元素。 |
peek(Consumer action) | 依次對每個元素執行一些操作,該方法返回的流與原有流包含相同的元素。該方法主要用於除錯。 |
distinct() | 該方法用於排序流中所有重複的元素(判斷元素重複的標準是使用 equals() 比較返回 true)。這是一個有狀態的方法。 |
sorted() | 該方法用於保證流中的元素在後續的訪問中處於有序狀態。這是一個有狀態的方法。 |
limit(long maxSize) | 該方法用於保證對該流的後續訪問中最大允許訪問的元素個數。這是一個有狀態的、短路方法。 |
下面簡單介紹一下 Stream 常用的末端方法
方法 | 說明 |
---|---|
forEach(Consumer action) | 遍歷流中所有元素,對每個元素執行action |
toArray() | 將流中所有元素轉換為一個數組 |
reduce() | 該方法有三個過載的版本,都用於通過某種操作來合併流中的元素 |
min() | 返回流中所有元素的最小值 |
max() | 返回流中所有元素的最大值 |
count() | 返回流中所有元素的數量 |
anyMatch(Predicate predicate) | 判斷流中是否至少包含一個元素符合 Predicate 條件。 |
allMatch(Predicate predicate) | 判斷流中是否每個元素都符合 Predicate 條件 |
noneMatch(Predicate predicate) | 判斷流中是否所有元素都不符合 Predicate 條件 |
findFirst() | 返回流中的第一個元素 |
findAny() | 返回流中的任意一個元素 |
對於前面的程式做一下stream優化的程式碼
import java.util.Collection;
import java.util.HashSet;
import java.util.stream.IntStream;
public class Test15 {
public void test2() {
// 建立一個集合
Collection objs = new HashSet();
objs.add(new String("C語言中文網Java教程"));
objs.add(new String("C語言中文網C++教程"));
objs.add(new String("C語言中文網C語言教程"));
objs.add(new String("C語言中文網Python教程"));
objs.add(new String("C語言中文網Go教程"));
// // 統計集合中出現“C語言中文網”字串的數量
// System.out.println(objs.stream().filter(ele -> ((String)ele).contains("C++教程")).count());
// // 統計集合中出現“Java”字串的數量
// System.out.println(objs.stream().filter(ele -> ((String) ele).contains("Java")).count()); // 輸出 1
// // 統計集合中出現字串長度大於 12 的數量
// System.out.println(objs.stream().filter(ele -> ((String) ele).length() > 12).count()); // 輸出 1
// 先呼叫Collection物件的stream ()方法將集合轉換為Stream
// 再呼叫Stream的mapToInt()方法獲取原有的Stream對應的IntStream
objs.stream().mapToInt(ele -> ((String) ele).length())
// 呼叫forEach()方法遍歷IntStream中每個元素
.forEach(System.out::println);// 輸出 11 11 12 10 14
}
public static void main(String[] args) {
new Test15().test2();
}
}