Promise進階——如何實現一個Promise庫

概述

從上次更新Promise/A+規范后,已經很久沒有更新博客了。之前由于業務需要,完成了一個TypeScript語言的Promise庫。這次我們來和大家一步一步介紹下,我們如何實現一個符合Promise/A+規范的Promise庫。

如果對Promise/A+規范還不太了解的同學,建議先看看上一篇博客——?[譯]前端基礎知識儲備——Promise/A+規范?。

實現流程

首先,我們來看下,在我實現的這一個Promise中,代碼由下面這幾部分組成:

  • 全局異步函數執行器
  • 常量與屬性
  • 類方法
  • 類靜態方法

通過上面這四個部分,我們就能夠得到一個完整的Promise。這四個部分互相有關聯,接下來我們一個一個模塊來看。

全局異步函數執行器

在之前的Promiz的源碼分析的博客中我有提到過,我們如何來實現一個異步函數執行器。通過JavaScript的執行原理我們可以知道,如果要實現異步執行相關函數的話,我們可以選擇使用宏任務和微任務,這一點在Promise/A+的規范中也有提及。因此,下面我們提供了一個用宏任務來實現異步函數執行器的代碼供大家參考。

let index = 0;if (global.postMessage) {global.addEventListener('message', (e) => {if (e.source === global) {let id = e.data;if (isRunningTask) {nextTick(functionStorage[id]);} else {isRunningTask = true;try {functionStorage[id]();} catch (e) {}isRunningTask = false;}delete functionStorage[id];functionStorage[id] = void 0;}});
}function nextTick(func) {if (global.setImmediate) {global.setImmediate(func);} else if (global.postMessage) {functionStorage[++index] = func;global.postMessage(index, '*')} else {setTimeout(func);}
}
復制代碼

通過上面的代碼我們可以看到,我們一共使用了setImmediatepostMessagesetTimeout這三個添加宏任務的方法來進行一步函數執行。

常量與屬性

說完了如何進行異步函數的執行,我們來看下相關的常量與屬性。在實現Promise之前,我們需要定義一些常量和類屬性,用于后面存儲數據。讓我們一個一個來看下。

常量

首先,Promise共有5個狀態,我們需要用常量來進行定義,具體如下:

enum State {pending = 0,resolving = 1,rejecting = 2,resolved = 3,rejected = 4
};
復制代碼

這五個常量分別對應Promise中的5個狀態,相信大家能夠從名字中理解,我們就不多講了。

屬性

在Promise中,我們需要一些屬性來存儲數據狀態和后續的Promise引用,具體如下:

class Promise {private _value;private _reason;private _next = [];public state: State = 0;public fn;public er;
}
復制代碼

我們對屬性進行逐一說明:

  • _value,表示在resolved狀態時,用來存儲當前的值。
  • _reason,表示在rejected狀態時,用來存儲當前的原因。
  • _next,表示當前Promise后面跟著then函數的引用。
  • fn,表示當前Promise中的then方法的第一個回調函數。
  • er,表示當前Promise中的then方法的的第二個回調函數(即catch的第一個參數,下面看catch實現方法就能理解)。

類方法

看完了常量與類的屬性,我們來看下類的靜態方法。

Constructor

首先,如果我們要實現一個Promise,我們需要一個構造函數來初始化最初的Promise。具體代碼如下:

class Promise {constructor(resolver?) {if (typeof resolver !== 'function' && resolver !== undefined) {throw TypeError()}if (typeof this !== 'object') {throw TypeError()}try {if (typeof resolver === 'function') {resolver(this.resolve.bind(this), this.reject.bind(this));}} catch (e) {this.reject(e);}}
}
復制代碼

從Promise/A+的規范來看,我們可以知道,如果resolver存在并且不是一個function的話,那么我們就應該拋出一個錯誤;否則,我們應該將resolvereject方法傳給resolver作為參數。

resolve && reject

那么,resolvereject方法又是做什么的呢?這兩個方法主要是用來讓當前的這個Promise轉換狀態的,即從pending狀態轉換為resolving或者rejecting狀態。下面讓我們來具體看下代碼:

class Promise {resolve(value) {if (this.state === State.pending) {this._value = value;this.state = State.resolving;nextTick(this._handleNextTick.bind(this));}return this;}reject(reason) {if (this.state === State.pending) {this._reason = reason;this.state = State.rejecting;this._value = void 0;nextTick(this._handleNextTick.bind(this));}return this;}
}
復制代碼

從上面的代碼中我們可以看到,當resolve或者reject方法被觸發時,我們都改變了當前這個Proimse的狀態,并且異步調用執行了_handleNextTick方法。狀態的改變標志著當前的Promise已經從pending狀態改變成了resolving或者rejecting狀態,而相應_value_reson也表示上一個Promise傳遞給下一個Promise的數據。

那么,這個_handleNextTick方法又是做什么的呢?其實,這個方法的作用很簡單,就是用來處理當前這個Promise后面跟著的then函數傳遞進來的回調函數fner

then && catch

在了解_handleNextTick之前,我們們先看下then函數和catch函數的實現。

class Promise {public then(fn, er?) {let promise = new Promise();promise.fn = fn;promise.er = er;if (this.state === State.resolved) {promise.resolve(this._value);} else if (this.state === State.rejected) {promise.reject(this._reason);} else {this._next.push(promise);}return promise;}public catch(er) {return this.then(null, er);}
}
復制代碼

因為catch函數調用就是一個then函數的別名,我們下面就只討論then函數。

then函數執行時,我們會創建一個新的Promise,然后將傳入的兩個回調函數用新的Promise的屬性保存下來。然后,先判斷當前的Promise的狀態,如果已經是resolved或者rejected狀態時,我們立即調用新的Promise中resolve或者reject方法,讓下將當前Promise的_value或者_reason傳遞給下一個Promise,并且觸發下一個Promise的狀態改變。如果當前Promise的狀態仍然為pending時,那么就將這個新生成的Promise保存下來,等當前這個Promise的狀態改變后,再觸發新的Promise變化。最后,我們返回了這個Promise的實例。

handleNextTick

看完了then函數,我們就可以來看下我們提到過的handleNextTick函數。

class Promise {private _handleNextTick() {try {if (this.state === State.resolving && typeof this.fn === 'function') {this._value = this.fn.call(getThis(), this._value);} else if (this.state === State.rejecting && typeof this.er === 'function') {this._value = this.er.call(getThis(), this._reason);this.state = 1;}} catch (e) {this.state = State.rejecting;this._reason = e;this._value = void 0;this._finishThisTypeScriptPromise();}// if promise === x, use TypeError to reject promise// 如果promise和x指向同一個對象,那么用TypeError作為原因拒絕promiseif (this._value === this) {this.state = State.rejecting;this._reason = new TypeError();this._value = void 0;}this._finishThisTypeScriptPromise();}
}
復制代碼

我們先來看一個簡單版的_handleNextTick函數,這樣能夠幫助我們快速理解Promise主流程。

異步觸發了_handleNextTick函數后,我們會判斷當前用戶處于的狀態,如果當前Promise是resolving狀態,我們就會調用fn函數,即我們在then函數調用時給新的Promise設置的那個fn函數;而如過當前Promise是rejecting狀態,我們就會調用er函數。

上面提到的getThis方法是用來獲取特定的this值,具體的規范要求我們將在稍后再進行介紹。

通過執行這兩個同步的fner函數,我們能夠得到當前Promise執行完傳入回調后的值。在這里需要說明的是:我們在執行fn或者er函數之前,我們在_value_reason中存放的值,是上一個Promise傳遞下來的值。只有當執行完了fn或者er函數后,_value_reason中存放的值才是我們需要傳遞給下一個Promise的值。

大家到這里可能會奇怪,我們的this指向沒有發生變化,但是為什么我們的this指向的是那個新的Promise,而不是原來的那個Promise呢?

我們可以從另外一個角度來看待這個問題:我們當前的這個Promise是不是由上一個Promise所產生的呢?如果是這種情況的話,我們就可以理解,在上一個Promise產生當前Promise的時候,就設置了fner兩個函數。

大家可能又會問,那么我們第一個Promise的fner這兩個參數是怎么來的呢?

那么我們就需要仔細看下上面這個邏輯了。下面我們只討論第一個Promise處于pending的情況,其余的情況與這種情形基本相同。第一個Promise因為沒有上一個Promise去設置fner兩個參數,因此這兩個參數的值就是undefined。所以在上面的邏輯中,我們已經排除了這種情況,直接進入了_finishThisTypeScriptPromise函數中。

我們在這里需要特別說明下的是,有些人會認為我們在調用then函數傳入的兩個回調函數fner時,當前Promise就結束了,其實并不是這樣,我們是得到了fn或者er兩個函數的返回值,再將值傳遞給下一個Promise時,上一個Promise才會結束。關于這個邏輯我們可以看下_finishThisTypeScriptPromise函數。

finishThisTypeScriptPromise

_finishThisTypeScriptPromise函數的代碼如下:

class Promise {private _finishThisTypeScriptPromise() {if (this.state === State.resolving) {this.state = State.resolved;this._next.map((nextTypeScriptPromise) => {nextTypeScriptPromise.resolve(this._value);});} else {this.state = State.rejected;this._next.map((nextTypeScriptPromise) => {nextTypeScriptPromise.reject(this._reason);});}}
}
復制代碼

_finishThisTypeScriptPromise函數中我們可以看到,我們在得到了需要傳遞給下一個Promise的_value或者_reason后,利用map方法逐個調用我們保存的新生成的Promise實例,調用它的resolve方法,因此我們又觸發了這個Promise的狀態從pending轉變為resolving或者rejecting

到這里我們就已經完全了解了一個Promise從最開始的創建,到最后結束的整個生命周期。下面我們來看下在Promise/A+規范中提到的一些分支邏輯的處理情況。

上一個Promise傳遞的value是Thenable實例

首先,讓我們來了解下什么是Thenable實例。Thenable實例指的是屬性中有then函數的對象。Promise就是的一種特殊的Thenable對象。

下面,為了方便講解,我們將用Promise來代替Thenable進行講解,其他的Thenable類大家可以參考類似思路進行分析。

如果我們在傳遞給我們的_value中是一個Promise實例,那么我們必須在等待傳入的Promise狀態轉換到resolved之后,當前的Promise才能夠繼續往下執行,即我們從傳入的Promise中得到了一個非Thenable返回值時,我們才能用這個值來調用屬性中的fn或者er方法。

那么,我們要怎么樣才能獲取到傳入的這個Promise的返回值呢?在Promise中其實用到了一個非常巧妙的方法:因為傳入的Promise中有一個then函數(Thenable定義),因此我們就調用then函數,在第一個回調函數fn中傳入獲取_value,觸發當前的Promise繼續執行。如果是觸發了第二個回調函數er,那么就用在er中得到的_reason來拒絕掉當前的Promise。具體判斷邏輯如下:

class Promise {private _handleNextTick() {let ref;let count = 0;try {// 判斷傳入的this._value是否為一個thanable// check if this._value a thenableref = this._value && this._value.then;} catch (e) {this.state = State.rejecting;this._reason = e;this._value = void 0;return this._handleNextTick();}if (this.state !== State.rejecting && (typeof this._value === 'object' || typeof this._value === 'function') && typeof ref === 'function') {// add a then function to get the status of the promise// 在原有TypeScriptPromise后增加一個then函數用來判斷原有promise的狀態try {ref.call(this._value, (value) => {if (count++) {return;}this._value = value;this.state = State.resolving;this._handleNextTick();}, (reason) => {if (count++) {return;}this._reason = reason;this.state = State.rejecting;this._value = void 0;this._handleNextTick();});} catch (e) {this.state = State.rejecting;this._reason = e;this._value = void 0;this._handleNextTick();}} else {try {if (this.state === State.resolving && typeof this.fn === 'function') {this._value = this.fn.call(getThis(), this._value);} else if (this.state === State.rejecting && typeof this.er === 'function') {this._value = this.er.call(getThis(), this._reason);this.state = 1;}} catch (e) {this.state = State.rejecting;this._reason = e;this._value = void 0;this._finishThisTypeScriptPromise();}this._finishThisTypeScriptPromise();}}
}
復制代碼

promise === value

在Promise/A+規范中,如果返回的_value值等于用戶自身時,需要用TypeError錯誤拒絕掉當前的Promise。因此我們需要在_handleNextTick中加入以下判斷代碼:

class Promise {private _handleNextTick() {let ref;let count = 0;try {// 判斷傳入的this._value是否為一個thanable// check if this._value a thenableref = this._value && this._value.then;} catch (e) {this.state = State.rejecting;this._reason = e;this._value = void 0;return this._handleNextTick();}if (this.state !== State.rejecting && (typeof this._value === 'object' || typeof this._value === 'function') && typeof ref === 'function') {// add a then function to get the status of the promise// 在原有TypeScriptPromise后增加一個then函數用來判斷原有promise的狀態...} else {try {if (this.state === State.resolving && typeof this.fn === 'function') {this._value = this.fn.call(getThis(), this._value);} else if (this.state === State.rejecting && typeof this.er === 'function') {this._value = this.er.call(getThis(), this._reason);this.state = 1;}} catch (e) {this.state = State.rejecting;this._reason = e;this._value = void 0;this._finishThisTypeScriptPromise();}// if promise === x, use TypeError to reject promise// 如果promise和x指向同一個對象,那么用TypeError作為原因拒絕promiseif (this._value === this) {this.state = State.rejecting;this._reason = new TypeError();this._value = void 0;}this._finishThisTypeScriptPromise();}}
}
復制代碼

getThis

在Promise/A+規范中規定:我們在調用fner兩個回調函數時,this的指向有限制。在嚴格模式下,this的值應該為undefined;在寬松模式下時,this的值應該為global

因此,我們還需要提供一個getThis函數用于處理上述情況。具體代碼如下:

class Promise {...
}function getThis() {return this;
}
復制代碼

類靜態方法

我們通過上面說到的類方法和一些特定分支的邏輯處理,我們就已經實現了一個符合基本功能的Promise類。那么,下面我們來看下ES6中提供的一些標準API我們如何來進行實現。具體API如下:

  • resolve
  • reject
  • all
  • race

下面我們就一個一個方法來看下。

resolve && reject

首先我們來看下最簡單的resolvereject方法。

class Promise {public static resolve(value?) {if (TypeScriptPromise._d !== 1) {throw TypeError();}if (value instanceof TypeScriptPromise) {return value;}return new TypeScriptPromise((resolve) => {resolve(value);});}public static reject(value?) {if (TypeScriptPromise._d !== 1) {throw TypeError();}return new TypeScriptPromise((resolve, reject) => {reject(value);});}
}
復制代碼

通過上面代碼我們可以看到,resolvereject方法基本上就是直接使用了內部的constructor方法進行Promise構建。

all

class Promise {public static all(arr) {if (TypeScriptPromise._d !== 1) {throw TypeError();}if (!(arr instanceof Array)) {return TypeScriptPromise.reject(new TypeError());}let promise = new TypeScriptPromise();function done() {// 統計還有多少未完成的TypeScriptPromise// count the unresolved promiselet unresolvedNumber = arr.filter((element) => {return element && element.then;}).length;if (!unresolvedNumber) {promise.resolve(arr);}arr.map((element, index) => {if (element && element.then) {element.then((value) => {arr[index] = value;done();return value;});}});}done();return promise;}
}
復制代碼

下面我們根據上面的代碼來簡單說下all函數的基本思路。

首先我們需要先創建一個新的Promise用于返回,保證后面用戶調用then函數進行后續邏輯處理時可以設置新Promise的fner這兩個回調函數。

然后,我們怎么獲取上面Promise數組中每一個Promise的值呢?方法很簡單,我們在前面就已經介紹過:我們調用了每一個Promise的then函數用來獲取當前這個Promise的值。并且,在每個Promise完成時,我們都檢查下是否所有的Promise都已經完成,如果已經完成,則觸發新Promise的狀態從pending轉換為resolving或者rejecting

race

class Promise {public static race(arr) {if (TypeScriptPromise._d !== 1) {throw TypeError();}if (!(arr instanceof Array)) {return TypeScriptPromise.reject(new TypeError());}let promise = new TypeScriptPromise();function done(value?) {if (value) {promise.resolve(value);}let unresolvedNumber = arr.filter((element) => {return element && element.then;}).length;if (!unresolvedNumber) {promise.resolve(arr);}arr.map((element, index) => {if (element && element.then) {element.then((value) => {arr[index] = value;done(value);return value;});}});}done();return promise;}
}
復制代碼

race的思路與all基本一致。只是我們在處理函數上不同。當我們只要檢測到數組中的Promise有一個已經轉換到了resolve或者rejected狀態(通過沒有then函數來進行判斷)時,就會立即出發新創建的Promise示例的狀態從pending轉換為resolving或者rejecting

總結

我們對Promise的異步函數執行器、常量與屬性、類方法、類靜態方法進行了逐一介紹,讓大家對整個Promise的構造和聲明周期有了一個深度的理解和認知。在整個開發中需要注意到的一些關鍵點和細節,我在上面也一一說明了。大家只需要按照這個思路,對照Promise/A+規范就能夠完成一個符合規范的Promise庫。

最后,給大家提供一個Promise/A+測試工具,大家實現了自己的Promise后,可以使用這個工具來測試是否完全符合整個Promise/A+規范。當然,大家如果想使用我的現成代碼,也歡迎大家使用我的代碼Github/typescript-proimse。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537228.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537228.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537228.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python中isinstance(3、object)_python中isinstance函數判斷各種類型的小細節

1. 基本語法isinstance(object, classinfo)Return true if the object argument is an instance of the classinfo argument, or of a (direct, indirect or virtual)subclass thereof. Also return true if classinfo is a type object (new-style class) and object is an ob…

[前端漫談] 做一個四則計算器

0x000 概述 近期重新開始學習計算機基礎方面的東西,比如計算機組成原理、網絡原理、編譯原理之類的東西,目前正好在學習編譯原理,開始對這一塊的東西感興趣,但是理論的學習有點枯燥無味,決定換種方式,那就是…

程序員筆試面試后上機_hcie面試有哪些要注意的事項?

大家都知道,華為認證hcie考試分為三個部分,分別是筆試、lab實驗和面試。其中,考生討論得最多的就是面試部分,因為面試不同于筆試和lab實驗,自己埋頭答題和操作就行,面試要面對考官,考核的東西非…

【Infragistics教程】在javascript構造函數中創建基本繼承

2019獨角獸企業重金招聘Python工程師標準>>> 【下載Infragistics Ultimate最新版本】 用javascript創建對象有四種方法。具體如下: 對象作為文本構造函數調用模式創建()方法在ES6之后使用類繼承的實現因對象創建方法而異。本文將解…

python爬蟲ssl錯誤_Python爬蟲:Requests的SSLError:certificate verify failed問題解決方案6條...

問題:腳本是用Python寫的,用到開源庫play-scraper,調用其collectionAPI來獲取Google Play的Top App列表。該庫使用了requests作為客戶端來對Google Play進行操作。當腳本執行時,會報如下錯誤:certificate verify faile…

2019年1月3日

數組 字面量創建數組 1. var arr[]; []里邊可以放數字,字符串,true,false,null,undefined,數組([1,2,3]),對象{x:1,y:2} var arr[1,2…

vertex 3.0 與SpringBoot混合開發之初探

SpringBoot是最近幾年比較流行的web應用開發框架,它是微服務的一個開發框架。它的Web服務器內核為Tomcat或Jetty,它們作為Servlet容量來對客戶端的http/https請求進行解析。最近,spring.io又出推出一套新的服務器內核框架,它就是W…

switch芯片和phy芯片的區別_感應式芯片卡CPU卡的FM1208-9和FM1208-10有什么區別,你知道嗎?...

感應式CPU卡是目前芯片卡中安全系統較高的芯片,使用范圍也較為廣泛,但是這款CPU分為FM1208-9和FM1208-10,那你們知道分別代表什么意思呢?他們之間有什么不同呢?CPU白卡FM是什么?首先,我們來說下…

每次登陸都要滑動驗證_湖人隊冠軍成員卡魯索很吃香:每次談判都有N支球隊點名要他...

10月24日NBA直播臺訊:洛杉磯湖人隊助理教練邁克-彭伯西在接受媒體采訪時透露,湖人隊替補控球后衛卡魯索目前在聯盟中很吃香。湖人隊每次進行交易談判時,對方球隊都點名想要卡魯索。彭伯西表示:“每一次我們在休賽期或者交易截止日…

[HAOI2015]按位或

樸素的 f[S]表示S到(1<<n)的期望次數 發現1的個數只增加不減少 所以可以類似拓撲序的圖&#xff0c;然后枚舉子集O(3^n)轉移 沒有優化的余地 另辟蹊徑&#xff1a; 拆開每一位來看 t[i]表示第i位變成1的次數 ansE(max(t[i])) 根據min-max容斥 得到&#xff1a;ans∑E(t[i…

MySQL在DOS指令里面的使用以及增刪改查的使用

本人的第一條博客&#xff0c;選中我的電腦單機右鍵&#xff0c;點開管理&#xff0c;選中服務找到MySQL57.啟動該服務。回退至桌面&#xff0c;按住winR 輸入cmd打開DOS指令的窗口。 在窗口輸入: mysql -h localhost -u root -p 顯示password輸入提示&#xff1a;表示已經…

node+socket.io 實現一個聊天室

我們只做簡單的實現&#xff0c;不接入數據庫&#xff0c;nodejs也不使用express和koa等框架 因此依賴只有兩個&#xff1a; 1、socket.io 2、mime&#xff08;用于獲取靜態資源時獲取文件的mime類型&#xff09; 安裝命令&#xff1a; npm install socket.io mime --save 其他…

安卓應用用戶數據_用戶指標數據應用

一、如何理解數據用戶數據&#xff1a;gender:性別、 birthday:出生日期行為數據&#xff1a;user_id:用戶id、auction_id:購買行為編號、buy_mount:購買數量、day:購買時間商品數據&#xff1a;cat_id:商品種類ID、cat1:商品類別、property:商品屬性二、用戶數據指標1.用戶數據…

三大數據庫數據庫端口號及連接jdbc驅動下載

Jdbc連接三大數據庫&#xff08;mysql sqlserver oracle&#xff09; Mysql:端口號為&#xff1a;3306&#xff08;默認&#xff09; 用java連接mysql數據庫 Try{Class.forName(“com.mysql.jdbc.Driver”); //DatabaseName:需要連接的數據庫名稱 String url”jdbc:mysql://12…

webgis從基礎到開發實踐_開源WebGIS教程系列——11.1 GISLite 的開發背景與設計

地理信息門戶可以幫助人們更容易地發現、訪問和使用地理空間信息&#xff0c; 是地理信息發布、服務和共享的重要環節。許多國家都很重視地理信息門戶的 建設&#xff0c;把它作為國家空間數據基礎設施(spatial data infrastructure&#xff0c;SDI)的重要組成部分。GISLite 是…

Oracle數據庫及在DOS命令下面的簡單操作

在Oracle數據庫注釋用--表明為注釋&#xff0c;但以下用//或--代表解釋;數據庫不怎么區分大小寫&#xff1b; 先說說一些簡單Oracle數據庫操作的語句&#xff1a; 使用語句創建普通用戶&#xff1a; Create user username identified by password; //創建普通用戶 Grant reso…

CSS屬性(display)

1.display屬性 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>08display屬性</title><style>.c1 {background-color: red;/*display: none; !* 讓其在頁面上不顯示 *!*//*display: i…

產品發布系統_【產品發布】第3期|閥門遙控系統

更多精彩&#xff0c;請點擊上方藍字關注我們&#xff01;常熟瑞特電氣股份有限公司的閥門遙控系統是一款經典的產品線&#xff0c;包括了全系列的液壓執行器&#xff0c;電液執行器&#xff0c;微型動力單元&#xff0c;液壓動力泵站&#xff0c;液壓電磁閥箱等產品。閥門遙控…

大數據就業前景,分析的太到位了

大數據廣泛應用于電網運行、經營管理及優質服務等各大領域&#xff0c;并正在改變著各行各業&#xff0c;也引領了大數據人才的變革。大數據就業前景怎么樣&#xff1f;這對于在就業迷途中的我們是一個很重要的信息。 隨著大數據時代的到來【這次國家教育部也改革動真格了】&am…

常用集合(List,Set,Map)的基本定義和操作

集合類存放于java.util包中。 集合類存放的都是對象的引用&#xff0c;而非對象本身&#xff0c;出于表達上的便利&#xff0c;我們稱集合中的對象就是指集合中對象的引用&#xff08;reference)。 常用的集合類型主要有3種&#xff1a;set(集&#xff09;、list(列表&#x…