- 導(dǎo)語(yǔ)
- List役电、Set赚爵、Map
- lambda表達(dá)式、流式操作
- 泛型
一法瑟、導(dǎo)語(yǔ)
? 在第一節(jié)課中冀膝,我們講了數(shù)組這一存儲(chǔ)多個(gè)元素的數(shù)據(jù)結(jié)構(gòu),可以方便我們?cè)诰幊讨杏靡粋€(gè)對(duì)象來(lái)儲(chǔ)存多個(gè)元素霎挟。然而在實(shí)際應(yīng)用時(shí)窝剖,數(shù)組有一些缺點(diǎn) ——
- 數(shù)組的長(zhǎng)度一旦確定就無(wú)法更改。
- 增加和刪除的效率低下酥夭。
- 根據(jù)內(nèi)容查找元素速率慢赐纱。
? 事實(shí)上,對(duì)于很多實(shí)際開發(fā)中的數(shù)據(jù)結(jié)構(gòu)熬北,單純用數(shù)組表示是比較麻煩且不易維護(hù)的千所,于是 Java 為我們提供了集合類來(lái)方便我們的操作。Java 集合類包含在 java.util 包下蒜埋,所以在使用集合類前淫痰,要添加導(dǎo)包語(yǔ)句 import java.util.*;
二、List整份、Set待错、Map
-
List
? 一個(gè) List 是一個(gè)元素有序的籽孙、可以重復(fù)、可以為 null 的集合(有時(shí)候我們也叫它“序列”)火俄。Java 集合框架中最常使用的幾種 List 的實(shí)現(xiàn)類是 ArrayList犯建,LinkedList 和 Vector。ArrayList 的特點(diǎn)是可以動(dòng)態(tài)添加元素瓜客,并像可以像數(shù)組一樣通過(guò)下標(biāo)訪問對(duì)應(yīng)的元素适瓦。我們接下來(lái)會(huì)介紹 ArrayList 的使用。
-
ArrayList
ArrayList是基于數(shù)組實(shí)現(xiàn)的List類谱仪,它封裝了一個(gè)動(dòng)態(tài)的增長(zhǎng)的玻熙、允許再分配的Object[]數(shù)組。public class ArrayListTest { public static void main(String[] args) { List<String> arrayList = new ArrayList<>(); //創(chuàng)建一個(gè) ArrayList 對(duì)象 arrayList.add("1"); arrayList.add("12"); arrayList.add("123"); //向 arrayList 的最后添加元素 arrayList.add(0, "0"); //在 arrayList 的指定下標(biāo)處插入元素 System.out.println(arrayList); String str = arrayList.get(1); //str = "1" //訪問 arrayList 指定下標(biāo)index對(duì)應(yīng)的元素 int length = arrayList.size(); //length = 4 //返回 arrayList 中元素的個(gè)數(shù) boolean isContain = arrayList.contains("123"); //isContain = true //判斷 arrayList 中是否存在某個(gè)元素值 arrayList.remove(0);//將arrayList中下標(biāo)為0的元素移除 System.out.println(arrayList); arrayList.remove("123");//將arrayList中"123"元素移除 System.out.println(arrayList); arrayList.clear();//將arrayList中所有元素移除 boolean isEmpty = arrayList.isEmpty();//isEmpty = true //判斷 arrayList 是否為空 } }
( 注:1. 在創(chuàng)建 ArrayList對(duì)象時(shí)疯攒,尖括號(hào)中<>若不填任何類型或接口嗦随,則 ArrayList 可以容納任何類型的元素,若規(guī)定了類型敬尺,則 ArrayList 只能容納該類型的對(duì)象枚尼。 2. 在調(diào)用 ArrayList 的 get 和 remove 方法時(shí),如果傳進(jìn)去的參數(shù) index ≥ arrayList.size() 或 index < 0 程序會(huì)停止運(yùn)行砂吞,并拋出異常(Exception)署恍。3. 在調(diào)用 ArrayList 的 remove 方法時(shí)(刪除 arrayList 中的某個(gè)元素), 如果原來(lái)的 ArrayList 中包含這個(gè)元素, ArrayList 會(huì)將這個(gè)元素刪除并返回 true蜻直,若不包含盯质,則直接返回false。)
-
Set
? Set 是對(duì)數(shù)學(xué)上集合概念的抽象袭蝗。一個(gè) Set 是一個(gè)元素?zé)o序的、不可以重復(fù)般婆,可以為 null 的集合到腥。與 List 不同的是,我們不能通過(guò)下標(biāo)來(lái)對(duì) Set 進(jìn)行操作蔚袍,且一個(gè) Set 最多可含一個(gè) null 元素乡范,對(duì)于任意的非 null 元素 e1 和 e2,都滿足
e1.equals(e2) == false
啤咽。Set 有三個(gè)實(shí)現(xiàn)類 —— HashSet晋辆、LinkedHashSet、TreeSet宇整。接下來(lái)以 HashSet 為例來(lái)介紹 Set 的用法瓶佳。
-
HashSet
HashSet是Set接口的典型實(shí)現(xiàn),HashSet使用HASH算法來(lái)存儲(chǔ)集合中的元素鳞青,因此具有良好的存取和查找性能霸饲。當(dāng)向HashSet集合中存入一個(gè)元素時(shí)为朋,HashSet會(huì)調(diào)用該對(duì)象的
值得主要的是,HashSet集合判斷兩個(gè)元素相等的標(biāo)準(zhǔn)是兩個(gè)對(duì)象通過(guò)equals()方法比較相等厚脉,并且兩個(gè)對(duì)象的hashCode()方法的返回值相等
ArrayList 中所有參數(shù)未涉及下標(biāo)(index)的函數(shù)都可以類推到 Set
兩個(gè) Set 取并集
set1.addAll(set2)/取并集的結(jié)果為 set1
兩個(gè) Set 取交集
set1.retainAll(set2)//取交集的結(jié)果為 set1
-
Set 的遍歷
public class HashSetTest { public static void main(String[] args) { HashSet set = new HashSet<String>(); //初始化一個(gè)Set set.add("12"); set.add("1"); set.add("12"); set.add("123"); set.add("123");//重復(fù)的元素不會(huì)被添加 set.add("13"); set.add("23"); set.add("42"); Iterator iterator = set.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } //輸出結(jié)果 //12 //1 //23 //123 //13 //42 //注:set是元素?zé)o序的 } }
-
Map
? Map 是對(duì)數(shù)學(xué)上映射概念的抽象习寸。Map 中的每個(gè)元素都包含兩部分 —— 鍵(key)和值(value),可以通過(guò)鍵來(lái)獲取值傻工。在一個(gè) Map 中霞溪,key 的值是不可以重復(fù)的。Map 有兩個(gè)實(shí)現(xiàn)類 —— HashMap中捆、TreeMap鸯匹。接下來(lái)以 HashMap 為例來(lái)介紹 Map 的用法。
-
HashMap
就像HashSet集合不能保證元素的順序一樣轨香,HashMap也不能保證key-value對(duì)的順序忽你。并且類似于HashSet判斷兩個(gè)key是否相等的標(biāo)準(zhǔn)也是: 兩個(gè)key通過(guò)equals()方法比較返回true、 同時(shí)兩個(gè)key的hashCode值也必須相等-
clear 臂容、size科雳、isEmpty 函數(shù)和 ArrayList 類似。
public class HashMapTest { public static void main(String[] args) { HashMap hashMap = new HashMap<Integer, String>();//初始化一個(gè)key類型為int,value類型為String的Map hashMap.put(1, "My key is 1."); hashMap.put(2, "My key is 2."); hashMap.put(3, "My key is 3."); //為map添加一個(gè)key為3脓杉,value為"My key is 3."的元素 System.out.println(hashMap.keySet()); // 輸出結(jié)果:[1, 2, 3] // 返回了hashMap中所有key的集合(Set) System.out.println(hashMap.values()); // 輸出結(jié)果:[My key is 1., My key is 2., My key is 3.] // 返回了hashMap中所有value的集合(Collection) System.out.println(hashMap.entrySet()); //輸出結(jié)果:[1=My key is 1., 2=My key is 2., 3=My key is 3.] // 返回了hashMap中所有映射關(guān)系的集合(Set) HashMap<Integer, String> hashMap1 = new HashMap<>(); hashMap1.put(4, "My key is 4"); hashMap1.put(5, "My key is 5"); hashMap.putAll(hashMap1);//將 hashMap1 中的所有元素加到hashMap中 System.out.println(hashMap.remove(1)); // 輸出結(jié)果:My key is 1. // 刪除 hashMap 中key為1的元素糟秘,并返回key為1的value值 } }
-
-
隊(duì)列
隊(duì)列的頭部保存著隊(duì)列中存放時(shí)間最長(zhǎng)的元素,隊(duì)列的尾部保存著隊(duì)列中存放時(shí)間最短的元素球散。新元素插入(offer)到隊(duì)列的尾部尿赚,訪問元素(poll)操作會(huì)返回隊(duì)列頭部的元素,隊(duì)列不允許隨機(jī)訪問隊(duì)列中的元素蕉堰。結(jié)合生活中常見的排隊(duì)就會(huì)很好理解這個(gè)概念三凌净、lambda表達(dá)式、流式操作符
Lambda表達(dá)式是Java SE 8中一個(gè)重要的新特性屋讶。lambda表達(dá)式允許你通過(guò)表達(dá)式來(lái)代替功能接口冰寻。 lambda表達(dá)式就和方法一樣,它提供了一個(gè)正常的參數(shù)列表和一個(gè)使用這些參數(shù)的主體
基本語(yǔ)法:
(parameters) -> expression
或
(parameters) ->{ statements;}
簡(jiǎn)例:
// 1. 不需要參數(shù),返回值為 5
() -> 5
// 2. 接收一個(gè)參數(shù)(數(shù)字類型),返回其2倍的值
x -> 2 * x
// 3. 接受2個(gè)參數(shù)(數(shù)字),并返回他們的差值
(x, y) -> x – y
// 4. 接收2個(gè)int型整數(shù),返回他們的和
(int x, int y) -> x + y
// 5. 接受一個(gè) string 對(duì)象,并在控制臺(tái)打印,不返回任何值(看起來(lái)像是返回void)
(String s) -> System.out.print(s)
看看用法:
interface Operation {
int operate(int a, int b);
}
public static int operate(int a, int b, Operation o){
o.operate(a, b);
}
public static void main(String []args){
Operation o = (int a, int b) -> a - b;
System.out.println(operate(3, 2, o)); //輸出 1
}
用lambda表達(dá)式實(shí)現(xiàn)匿名內(nèi)部類(Runnable 接口)
// 1.1使用匿名內(nèi)部類
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();
// 1.2使用 lambda expression
new Thread(() -> System.out.println("Hello world !")).start();
集合的流式操作:
Stream :
Java 8 中的 Stream 是對(duì)集合(Collection)對(duì)象功能的增強(qiáng),它專注于對(duì)集合對(duì)象進(jìn)行各種非常便利皿渗、高效的聚合操作(aggregate operation)斩芭,或者大批量數(shù)據(jù)操作 (bulk data operation)。Stream API 借助于同樣新出現(xiàn)的 Lambda表達(dá)式乐疆,極大的提高編程效率和程序可讀性划乖。
示例:
List<StringClass> list1 = new ArrayList<>();
FirstData first = new FirstData();
for (int i = 0; i < first.getDataList().size(); i++){
SecondData secondData = first.getDataList().get(i);
//do something with second and firstData.
for (int j = 0; j < secondData.getDataList().size(); j++ ){
ThirdData third = secondData.getDataList().get(j);
//give thirdList a filter
if (third.getThirdData().startsWith(" ")){
list1.add(new StringClass(third.getThirdData()));
}
//do something with third and thirdData
}
}
first.getDataList()
.forEach(it ->
it.getDataList()
.stream()
.filter(t -> t.getThirdData().startsWith(" "))
.map(t -> new StringClass(t.getThirdData()))
.forEach(list1::add)
);
四、泛型
? Java 泛型(generics)是 JDK 5 中引入的一個(gè)新特性, 泛型提供了編譯時(shí)類型安全檢測(cè)機(jī)制挤土,該機(jī)制允許程序員在編譯時(shí)檢測(cè)到非法的類型琴庵。使用泛型,可以增加我們代碼的復(fù)用性。
先看一個(gè)例子:
List arrayList = new ArrayList</*String*/>();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
System.out.println("item = "+item);
}
毫無(wú)疑問细卧,程序的運(yùn)行結(jié)果會(huì)以崩潰結(jié)束:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
ArrayList可以存放任意類型尉桩,例子中添加了一個(gè)String類型,添加了一個(gè)Integer類型贪庙,再使用時(shí)都以String的方式使用蜘犁,因此程序崩潰了。為了解決類似這樣的問題(在編譯階段就可以解決)止邮,泛型應(yīng)運(yùn)而生这橙。
我們將第一行聲明初始化list的代碼更改一下,編譯器會(huì)在編譯階段就能夠幫我們發(fā)現(xiàn)類似這樣的問題导披。
List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); 在編譯階段屈扎,編譯器就會(huì)報(bào)錯(cuò)
? 泛型的本質(zhì)是參數(shù)化類型,也就是說(shuō)所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)撩匕。
?
-
泛型類
? 泛型類的聲明和非泛型類的聲明類似鹰晨,除了在類名后面添加了類型參數(shù)聲明部分。
? 和泛型方法一樣止毕,泛型類的類型參數(shù)聲明部分也包含一個(gè)或多個(gè)類型參數(shù)模蜡,參數(shù)間用逗號(hào)隔開。一個(gè)泛型參數(shù)扁凛,也被稱為一個(gè)類型變量忍疾,是用于指定一個(gè)泛型類型名稱的標(biāo)識(shí)符。因?yàn)樗麄兘邮芤粋€(gè)或多個(gè)參數(shù)谨朝,這些類被稱為參數(shù)化的類或參數(shù)化的類型卤妒。
實(shí)例
public class Box<T> { public Box(T t){ this.t = t; } private T t; public void set(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Box<Integer> integerBox = new Box<>(); Box<String> stringBox = new Box<>(); integerBox.set(10); stringBox.set("天外天移動(dòng)Android組"); System.out.println(integerBox.get()); System.out.println(stringBox.get()); //輸出結(jié)果: //10 //天外天移動(dòng)Android組 } }
定義的泛型類,就一定要傳入泛型類型實(shí)參么字币?并不是這樣则披,在使用泛型的時(shí)候如果傳入泛型實(shí)參,則會(huì)根據(jù)傳入的泛型實(shí)參做相應(yīng)的限制洗出,此時(shí)泛型才會(huì)起到本應(yīng)起到的限制作用士复。如果不傳入泛型類型實(shí)參的話,在泛型類中使用泛型的方法或成員變量定義的類型可以為任何的類型共苛。
public class Box<T> {
private T t;
public Box(T t){
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box integerBox = new Box<>(10);
Box stringBox = new Box<>("天外天移動(dòng)Android組");
System.out.println(integerBox.get());
System.out.println(stringBox.get());
//輸出結(jié)果:
//10
//天外天移動(dòng)Android組
}
}
-
泛型接口
泛型接口與泛型類的定義及使用基本相同判没。泛型接口常被用在各種類的生產(chǎn)器中蜓萄,可以看一個(gè)例子
//定義一個(gè)泛型接口
public interface Generator<T> {
public T next();
}
當(dāng)實(shí)現(xiàn)泛型接口的類隅茎,傳入泛型實(shí)參時(shí):
/**
* 傳入泛型實(shí)參時(shí):
* 定義一個(gè)生產(chǎn)器實(shí)現(xiàn)這個(gè)接口,雖然我們只創(chuàng)建了一個(gè)泛型接口Generator<T>
* 但是我們可以為T傳入無(wú)數(shù)個(gè)實(shí)參,形成無(wú)數(shù)種類型的Generator接口嫉沽。
* 在實(shí)現(xiàn)類實(shí)現(xiàn)泛型接口時(shí)辟犀,如已將泛型類型傳入實(shí)參類型,則所有使用泛型的地方都要替換成傳入的實(shí)參類型
* 即:Generator<T>绸硕,public T next();中的的T都要替換成傳入的String類型堂竟。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
- 類型通配符
在Box類的基礎(chǔ)上魂毁,我們?cè)黾右粋€(gè)函數(shù),將box之中的值打印出來(lái):
//不是一個(gè)泛型方法
public void showKeyValue1(Box<?> obj){
Log.d("泛型測(cè)試","key value is " + obj.getKey());
}
此處的出嘹?就是類型通配符席楚,可以讓我們傳入不同泛型對(duì)應(yīng)的Box類。
-
泛型方法
你可以寫一個(gè)泛型方法税稼,該方法在調(diào)用時(shí)可以接收不同類型的參數(shù)烦秩。根據(jù)傳遞給泛型方法的參數(shù)類型,編譯器適當(dāng)?shù)靥幚砻恳粋€(gè)方法調(diào)用郎仆。
下面是定義泛型方法的規(guī)則:
所有泛型方法聲明都有一個(gè)類型參數(shù)聲明部分(由尖括號(hào)分隔)只祠,該類型參數(shù)聲明部分在方法返回類型之前(在下面例子中的<T>)。
每一個(gè)類型參數(shù)聲明部分包含一個(gè)或多個(gè)類型參數(shù)扰肌,參數(shù)間用逗號(hào)隔開抛寝。一個(gè)泛型參數(shù),也被稱為一個(gè)類型變量曙旭,是用于指定一個(gè)泛型類型名稱的標(biāo)識(shí)符盗舰。
類型參數(shù)能被用來(lái)聲明返回值類型,并且能作為泛型方法得到的實(shí)際參數(shù)類型的占位符夷狰。
泛型方法體的聲明不能是原始類型(比如int 岭皂、double、char)沼头。
實(shí)例
public class GenericsMethodTest {
/**
* 這才是一個(gè)真正的泛型方法爷绘。
* 首先在public與返回值之間的<T>必不可少,這表明這是一個(gè)泛型方法进倍,并且聲明了一個(gè)泛型T
* 這個(gè)T可以出現(xiàn)在這個(gè)泛型方法的任意位置.
* 泛型的數(shù)量也可以為任意多個(gè)
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
private static<T> void printClassName(T t){
System.out.println(t.getClass());
}
public static void main(String[] args) {
printClassName(1);
printClassName(2.0);
printClassName("twt");
//輸出結(jié)果:
//class java.lang.Integer
//class java.lang.Double
//class java.lang.String
}
}
- 泛型上下邊界
在使用泛型的時(shí)候土至,我們還可以為傳入的泛型類型實(shí)參進(jìn)行上下邊界的限制,如:類型實(shí)參只準(zhǔn)傳入某種類型的父類或某種類型的子類猾昆。
- 為泛型通配符添加上邊界陶因,即傳入的類型實(shí)參必須是指定類型的子類型
public void showKeyValue1(Box<? extends Number> obj){
Log.d("泛型測(cè)試","key value is " + obj.getKey());
}
Box<String> box1 = new Box<String>("11111");
Box<Integer> box2 = new Box<Integer>(2222);
Box<Float> box3 = new Box<Float>(2.4f);
Box<Double> box4 = new Box<Double>(2.56);
//這一行代碼編譯器會(huì)提示錯(cuò)誤,因?yàn)镾tring類型并不是Number類型的子類
//showKeyValue1(box1);
showKeyValue1(box2);
showKeyValue1(box3);
showKeyValue1(box4);
- 為方法的泛型添加上邊界
//在泛型方法中添加上下邊界限制的時(shí)候垂蜗,必須在權(quán)限聲明與返回值之間的<T>上添加上下邊界楷扬,即在泛型聲明的時(shí)候添加
//public <T> T showKeyName(Generic<T extends Number> container),編譯器會(huì)報(bào)錯(cuò):"Unexpected bound"
public <T extends Number> T showKeyName(Box<T> container){
System.out.println("container key :" + container.getKey());
T test = container.getKey();
return test;
}
代碼來(lái)源
本文中的所有代碼均來(lái)自自己寫的一個(gè) Java demo贴见,已上傳至 github 烘苹。