Android Utils 之 Vector 學習筆記(一)—— VectorImpl 程式碼分析
前言
在維護 Android Framework 層時,常常能看到對 KeyedVector
與 DefaultKeyedVector
的使用。並且我們內部的服務框架也有用到 KeyedVector
這個結構。但在維護專案的過程中,我們發現它可能會導致資料處理順序出現混亂的情況,我認為這一定是因為我們對這個結構不夠了解導致的。關於這個結構內部的具體實現,我覺得我有必要去了解清楚,以便在後續的使用中能夠發揮出它的最大功效,並且避免一些出問題的情況。
根據引用的標頭檔案名,我們可以到 Android 原始碼目錄下的 system/core/libutils/include
中找到相應的標頭檔案。
瀏覽相關標頭檔案,可以看到在 Android 內建的 Utils 中,有四種可用的 Vector 結構:
Vector
SortedVector
KeyedVector
DefaultKeyedVector
這些類之間的關係如下圖:
由圖可知:
Vector
與SortedVector
分別繼承自抽象類VectorImpl
與SortedVectorImpl
(而後者又是前者的子類),這兩個抽象類實現了一些重要的底層操作。Vector
與SortedVector
之間具有依賴關係(後者成員函式中有前者型別的引數)。KeyedVector
相對獨立,它與SortedVector
是關聯關係(前者有一個後者型別的成員變數)。DefaultKeyedVector
繼承自KeyedVector
,而根據程式碼來看,它們之間沒有多少差異。
首先要讀懂 VectorImpl
與 SortedVectorImpl
,然後再看 Vector
與 SortedVector
。最後就要看我的重點 KeyedVector
,這時候應該就有一個比較清晰的理解了。DefaultKeyedVector
差別不大,和 KeyedVector
一起看就行。
相關文章
- Android Utils 之 Vector 學習筆記(一)—— VectorImpl 程式碼分析
- Android Utils 之 Vector 學習筆記(二)—— SortedVectorImpl 程式碼分析
- Android Utils 之 Vector 學習筆記(三)—— Vector 與 SortedVector 程式碼分析
- Android Utils 之 Vector 學習筆記(四,完結)—— KeyedVector 與 DefaultKeyedVector 程式碼分析
標頭檔案
檔案路徑:system\core\libutils\include\utils\VectorImpl.h
概覽:
- 開頭的註釋內容:這個類是實現
Vector
類的核心部分。它能保證逆向二進位制相容,並且減少程式碼量。考慮效能原因,將mStorage
與mCount
兩個內部欄位暴露出來,它們是固定不變的。 - 第 13~17 行:傳入建構函式的標誌位,表示是否為
trivial
型別。其中,ctor
表示建構函式,dtor
即是析構,copy
則表示拷貝。 - 第 24 行:子類析構時必須呼叫的函式,涉及到記憶體空間的釋放與內部成員重置。
- 第 29 行:用於以 C 語言陣列風格返回 Vector 的訪問指標,但不能對其進行編輯操作。
- 第 30 行:用於以 C 語言陣列風格返回 Vector,並且可以對內容進行編輯。
- 第 40~43 行:用於往當前 Vector 中插入(或末尾追加)另一個 Vector(或陣列)。
- 第 46~57 行:用於增加、插入、替換以及刪除 Vector 中的單個元素。
- 第 58 行:用於清空(重置) Vector。
- 第 60、61 行:用於查詢
index
位置對應的元素,注意名帶 edit 的返回的是可編輯的結果。 - 第 63~66 行:定義兩個函式指標,用於指向自定義的比較函式。
sort
函式則是基於比較函式進行排序。 - 第 70 行:用於釋放
mStorage
的儲存空間。 - 第 72~77 行:一組純虛擬函式,在子類中必須進行具體實現。
- 第 80、81 行:用於從 Vector 的
where
位置開始擴充(或收縮)。 - 第 83~88 行:這些行內函數與前面的那組純虛擬函式是一一對應的,各自的功能已經由其函式名錶述清楚了。
- 第 92~96 行:內部成員變數,分別儲存了 Vector 資料首地址、元素總數、關於
trivial
型別的標誌,以及單個元素的資料大小。
/*!
* Implementation of the guts of the vector<> class
* this ensures backward binary compatibility and
* reduces code size.
* For performance reasons, we expose mStorage and mCount
* so these fields are set in stone.
*
*/
class VectorImpl
{
public:
enum { // flags passed to the ctor
HAS_TRIVIAL_CTOR = 0x00000001,
HAS_TRIVIAL_DTOR = 0x00000002,
HAS_TRIVIAL_COPY = 0x00000004,
};
VectorImpl(size_t itemSize, uint32_t flags);
VectorImpl(const VectorImpl& rhs);
virtual ~VectorImpl();
/*! must be called from subclasses destructor */
void finish_vector();
VectorImpl& operator = (const VectorImpl& rhs);
/*! C-style array access */
inline const void* arrayImpl() const { return mStorage; }
void* editArrayImpl();
/*! vector stats */
inline size_t size() const { return mCount; }
inline bool isEmpty() const { return mCount == 0; }
size_t capacity() const;
ssize_t setCapacity(size_t size);
ssize_t resize(size_t size);
/*! append/insert another vector or array */
ssize_t insertVectorAt(const VectorImpl& vector, size_t index);
ssize_t appendVector(const VectorImpl& vector);
ssize_t insertArrayAt(const void* array, size_t index, size_t length);
ssize_t appendArray(const void* array, size_t length);
/*! add/insert/replace items */
ssize_t insertAt(size_t where, size_t numItems = 1);
ssize_t insertAt(const void* item, size_t where, size_t numItems = 1);
void pop();
void push();
void push(const void* item);
ssize_t add();
ssize_t add(const void* item);
ssize_t replaceAt(size_t index);
ssize_t replaceAt(const void* item, size_t index);
/*! remove items */
ssize_t removeItemsAt(size_t index, size_t count = 1);
void clear();
const void* itemLocation(size_t index) const;
void* editItemLocation(size_t index);
typedef int (*compar_t)(const void* lhs, const void* rhs);
typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state);
status_t sort(compar_t cmp);
status_t sort(compar_r_t cmp, void* state);
protected:
size_t itemSize() const;
void release_storage();
virtual void do_construct(void* storage, size_t num) const = 0;
virtual void do_destroy(void* storage, size_t num) const = 0;
virtual void do_copy(void* dest, const void* from, size_t num) const = 0;
virtual void do_splat(void* dest, const void* item, size_t num) const = 0;
virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0;
virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0;
private:
void* _grow(size_t where, size_t amount);
void _shrink(size_t where, size_t amount);
inline void _do_construct(void* storage, size_t num) const;
inline void _do_destroy(void* storage, size_t num) const;
inline void _do_copy(void* dest, const void* from, size_t num) const;
inline void _do_splat(void* dest, const void* item, size_t num) const;
inline void _do_move_forward(void* dest, const void* from, size_t num) const;
inline void _do_move_backward(void* dest, const void* from, size_t num) const;
// These 2 fields are exposed in the inlines below,
// so they're set in stone.
void * mStorage; // base address of the vector
size_t mCount; // number of items
const uint32_t mFlags;
const size_t mItemSize;
};
具體實現
檔案路徑:system\core\libutils\VectorImpl.cpp
如果對所有函式的實現都一一解釋,那樣太浪費時間了,所以我只選擇一些我認為比較重要的函式實現進行分析。
基本的元素操作(增刪查詢)就不說了,接下來我們分析這幾個函式:
- 排序函式:
sort
- 擴充函式:
_grow
- 收縮函式:
_shrink
首先來看看一個常量 kMinVectorCapacity
,顧名思義,這定義的是 Vector 的最小容量,也是其初始容量。可以看到 Android 中將其設定為 4
。
const size_t kMinVectorCapacity = 4;
雖然在前面有提到一些函式的用途,但在這裡我還是要再描述一遍,因為如果不瞭解這些函式的功能,後面的分析可能就難以看下去了。
函式功能簡述
我們可以分成幾個大類來看:
- (Public)提供 Vector 的陣列式訪問:
arrayImpl
:返回不可編輯的陣列。editArrayImpl
:返回可編輯的資料。
- (Public)關於 Vector 資料量的操作:
size
:返回當前資料量大小,值得一提的是,它是通過mStorage
的空間大小與設定的元素大小mItemSize
計算出來的。isEmpty
:判斷當前是否無資料。capacity
:返回當前 Vector 的容量。setCapacity
:設定 Vector 的容量(成功則返回新容量數值,失敗則返回相應錯誤碼)。resize
:調整當前資料量大小。若調整值size
大於當前資料量,則從資料末端增加元素直到資料量大小等於size
;若size
小於當前資料量,則從資料末端向前刪除元素直到資料量大小等於size
(成功則返回新資料量大小,失敗返回相應錯誤碼)。
- (Public)Vector 或陣列之間的合併操作:
insertVectorAt
/insertArrayAt
:在當前 Vector 的某一位置插入另一個 Vector 或陣列。appendVector
/appendArray
:向當前 Vector 末端追加另一個 Vector 或陣列。
- (Public)Vector 中單個元素的相關操作:
insertAt
:在指定位置插入元素。pop
:彈出(刪除)Vector 末尾的一個元素。push
:在 Vector 末尾放入(插入)一個元素。add
:與push
一樣,末端插入一個元素,但該函式有返回值,若動作失敗則返回錯誤碼。replaceAt
:將指定位置的元素替換為自定義的元素。removeItemsAt
:刪除指定位置的元素。itemLocation
:返回指定位置的元素,不可編輯。editItemLocation
:返回指定位置的元素,可編輯。
- 記憶體釋放回收操作:
finish_vector
:釋放mStorage
儲存空間,並將其與mCount
重置為0
。(在子類的解構函式中必須呼叫該函式以防記憶體洩露)clear
:清除 Vector 資料並重置,減容至最小狀態。release_storage
:釋放mStorage
的儲存空間。
- (Private)內部使用的底層操作:
_do_construct
:若mFlags
中沒有HAS_TRIVIAL_CTOR
標記,則呼叫do_construct
對mStorage
進行構造操作。反之則不進行任何動作。_do_destroy
:與構造類似(判斷HAS_TRIVIAL_DTOR
標記),但這一函式是用於析構mStorage
的。_do_copy
:將from
指向的地址開始的num
個元素拷貝到dest
指向的地址。判斷HAS_TRIVIAL_COPY
標記,若有則直接使用memcpy
進行拷貝,若無則呼叫do_copy
進行拷貝。_do_splat
:呼叫do_splat
實現 Vector 分裂操作(在指定位置插入元素時會用到,首先在指定位置進行擴容,然後將用do_splat
將元素覆蓋到該位置對應的記憶體空間上)。_do_move_forward
:呼叫do_move_forward
將從from
處開始的num
個元素正向移動到記憶體位置dest
(當兩塊記憶體位置有重疊時,移動後from
的內容可能會發生改變)。_do_move_backward
:與向前移動類似,呼叫do_move_backward
將從from
處開始的num
個元素反向移動到記憶體位置dest
。
排序函式分析
排序函式(插入排序)主要思想:第 i
個元素之前的陣列已經是按規則排序好的了,現在要將其插入到已排序陣列中的合適位置。我們從後往前依次尋找這個位置,在尋找過程中同時也將需要移動的元素向後進行移動,這樣只要一找到恰當的位置,就完成了一輪排序。只要依次遍歷所有元素,則整個 Vector 的排序也就完成了。
圖解排序:
程式碼分析:
- 第 1~4 行:定義一個靜態函式
sortProxy
,用於通過函式指標func
呼叫對應的比較函式。 - 第 6~9 行:這個
sort
應是由外部呼叫的,它會進一步呼叫具體實現的sort
函式。 - 第 11 行:注意到傳入的引數
cmp
,它對應的是上面的sortProxy
函式;而引數state
則對應的是自定義的具體的比較函式的函式指標。在該函式中呼叫cmp
時,傳入的引數是需要比較的兩個元素以及state
對應的函式指標,最終效果就是呼叫自定義比較函式比較兩個傳入的元素。 - 第 13~15 行(註釋翻譯):該排序必須是穩定的。我們目前使用的是插入排序,這種方法對於小規模(以及已排序的陣列)來說非常合適。而對於大規模陣列來說,使用歸併排序可能會更好。
- 第 16、17 行:當 Vector 中資料量大於
1
時,排序才有意義。 - 第 18 行:初始化指標
array
。由於呼叫的是arrayImpl
函式來獲取陣列訪問地址,而隨後需要對array
進行強制型別轉換操作,所以需要把arrayImpl
返回值的const
限制移除(為什麼不直接呼叫editArrayImpl
呢…)。 - 第 21 行:開始從頭至尾遍歷每個元素進行排序。
- 第 22、23 行:獲取陣列中第
i
個元素item
,以及第i-1
個元素curr
。此處array
強制型別轉換為char*
型別(對其加一就是向後移動一個位元組),這樣就能保證+
操作獲取到的是相應元素的首地址。 - 第 24 行:通過呼叫
cmp
比較curr
與item
兩個元素,我們可以認為這表示的是當curr
大於item
時進行接下來的排序操作(實際使用時需要根據cmp
的具體實現來判斷)。 - 第 28~33 行:這是最開始的一次比較時會進行的操作,呼叫
editArrayImpl
給指標array
賦予陣列首地址,然後申請一份用於臨時存放需要轉移的元素的空間,temp
指標指向其首地址。然後獲取第i
個與第i-1
個元素首地址。 - 第 35 行:第二次比較以後,都會先清除
temp
指向的記憶體中的資料,但不會釋放這些記憶體。 - 第 38 行:將
item
指向的元素拷貝至temp
指向的臨時空間中。 - 第 40~51 行:插入排序的核心部分。
- (a) 首先令
next
指標指向第i
個元素(即前面的item
,而此時item
對應的資料已經拷貝到temp
指向的位置了)。 - (b) 進入迴圈,首先將
next
位置的元素資料銷燬,然後把前一個元素curr
的資料拷貝到next
的位置。 - (c) 令
next
指向curr
的位置,即next
向前移動一個元素。 - (d) 變數
j
減1
,然後將curr
指向陣列的第j
個元素位置,即curr
也向前移動一個元素。 - (e) 繼續比較
curr
與next
,若curr
大於next
,則回到步驟 (b)。
- (a) 首先令
- 第 53、54 行:清除當前
next
指向的元素,並將temp
中資料拷貝過去。(此時的next
即是元素i
在這一輪排序中最終的位置) - 第 56 行:對陣列中下一個元素進行插入排序。
- 第 59~62 行:此時排序已經完全結束,則需要銷燬
temp
中的臨時資料,並釋放其對應的臨時記憶體空間。
static int sortProxy(const void* lhs, const void* rhs, void* func)
{
return (*(VectorImpl::compar_t)func)(lhs, rhs);
}
status_t VectorImpl::sort(VectorImpl::compar_t cmp)
{
return sort(sortProxy, (void*)cmp);
}
status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state)
{
// the sort must be stable. we're using insertion sort which
// is well suited for small and already sorted arrays
// for big arrays, it could be better to use mergesort
const ssize_t count = size();
if (count > 1) {
void* array = const_cast<void*>(arrayImpl());
void* temp = 0;
ssize_t i = 1;
while (i < count) {
void* item = reinterpret_cast<char*>(array) + mItemSize*(i);
void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
if (cmp(curr, item, state) > 0) {
if (!temp) {
// we're going to have to modify the array...
array = editArrayImpl();
if (!array) return NO_MEMORY;
temp = malloc(mItemSize);
if (!temp) return NO_MEMORY;
item = reinterpret_cast<char*>(array) + mItemSize*(i);
curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);
} else {
_do_destroy(temp, 1);
}
_do_copy(temp, item, 1);
ssize_t j = i-1;
void* next = reinterpret_cast<char*>(array) + mItemSize*(i);
do {
_do_destroy(next, 1);
_do_copy(next, curr, 1);
next = curr;
--j;
curr = NULL;
if (j >= 0) {
curr = reinterpret_cast<char*>(array) + mItemSize*(j);
}
} while (j>=0 && (cmp(curr, temp, state) > 0));
_do_destroy(next, 1);
_do_copy(next, temp, 1);
}
i++;
}
if (temp) {
_do_destroy(temp, 1);
free(temp);
}
}
return NO_ERROR;
}
擴充函式分析
擴充函式的主要邏輯:在 Vector 的指定位置 where
處(這個位置不能大於 mCount
)開始,騰出 amount
個元素的空間(將原先從 where
處開始到末端的元素向後移動 amount
個位置),若判定容量不足以支援本次擴充,則會進行相應的擴容。
NOTE:擴充表示的是將 Vector 當前大小(size)擴大,而擴容則是將 Vector 的容量(capacity)增大。
圖解擴充:
程式碼分析:
- 第 1 行:注意傳入的引數意義,
where
是指定的擴充位置,amount
則是需要擴充的總個數。 - 第 6~8 行:引數有效性檢測,
where
決不能大於mCount
,否則觸發中斷。 - 第 11 行:計算新的 Vector 大小,此處用
safe_add
防止溢位,溢位時會觸發錯誤 LOG。 - 第 13 行:如果新的大小大於 Vector 容量,則接下來需要有一些擴容操作。
- 第 21~26 行:新容量的計算,可以看出來
new_capacity = max(4, x + (x/2) + 1)
,其中每次加操作都需要呼叫safe_add
完成以防止溢位(公式中有一個1u
,這表示unsigned
型的1
)。 - 第 28、29 行:計算需要申請的記憶體大小,呼叫
safe_mul
計算new_alloc_size = new_capacity * mItemSize
以防止溢位。 - 第 33~44 行:一個比較特殊的情況下的擴容處理。
- 條件:
mStorage
不為空,where
指向 Vector 末端,同時copy
與dtor
都是trivial
型別的。 - 通過
SharedBuffer
為mStorage
調整給其分配的記憶體空間。 - 注意到此時不用進行資料拷貝之類的操作,所以調整記憶體完畢後就結束了。
- 條件:
- 第 45~62 行:通常情況下的擴容操作。
- 首先從
SharedBuffer
中申請相應的空間,並讓指標array
指向空間首地址。 - 將當前
mStorage
中第0 ~ where
個資料拷貝到array
指向的空間中。 - 若
where
小於count
,則將mStorage
中的從where
處開始直到末端的元素拷貝到array
中的where + amount
位置(其實相當於向後移動了amount
個位置)。 - 清空當前
mStorage
的資料,釋放對應空間,並讓其指向array
的地址。
- 首先從
- 第 63~70 行:無需擴容的情況,只需要將
where
處直到末端的元素向後(正向)移動amount
個位置即可。
void* VectorImpl::_grow(size_t where, size_t amount)
{
// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
// this, (int)where, (int)amount, (int)mCount, (int)capacity());
ALOG_ASSERT(where <= mCount,
"[%p] _grow: where=%d, amount=%d, count=%d",
this, (int)where, (int)amount, (int)mCount); // caller already checked
size_t new_size;
LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow");
if (capacity() < new_size) {
// NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)
// (sigh..). Also note, the " + 1" was necessary to handle the special case
// where x == 1, where the resized_capacity will be equal to the old
// capacity without the +1. The old calculation wouldn't work properly
// if x was zero.
//
// This approximates the old calculation, using (x + (x/2) + 1) instead.
size_t new_capacity = 0;
LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)),
"new_capacity overflow");
LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)),
"new_capacity overflow");
new_capacity = max(kMinVectorCapacity, new_capacity);
size_t new_alloc_size = 0;
LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize),
"new_alloc_size overflow");
// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
if ((mStorage) &&
(mCount==where) &&
(mFlags & HAS_TRIVIAL_COPY) &&
(mFlags & HAS_TRIVIAL_DTOR))
{
const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
SharedBuffer* sb = cur_sb->editResize(new_alloc_size);
if (sb) {
mStorage = sb->data();
} else {
return NULL;
}
} else {
SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
if (sb) {
void* array = sb->data();
if (where != 0) {
_do_copy(array, mStorage, where);
}
if (where != mCount) {
const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
_do_copy(dest, from, mCount-where);
}
release_storage();
mStorage = const_cast<void*>(array);
} else {
return NULL;
}
}
} else {
void* array = editArrayImpl();
if (where != mCount) {
const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
_do_move_forward(to, from, mCount - where);
}
}
mCount = new_size;
void* free_space = const_cast<void*>(itemLocation(where));
return free_space;
}
收縮函式分析
收縮函式主要邏輯:從 where
處開始,將連續的 amount
個元素銷燬(需要保證有這麼多可銷燬的元素),由於 Vector 收縮了,其容量可能也需要根據一定規律進行減容,以減少不必要的記憶體佔用。
圖解收縮:
程式碼分析:
- 第 9~11 行:引數有效性判定,要保證存在足夠的可銷燬元素。
- 第 14 行:計算收縮後的 Vector 大小,公式為
new_size = mCount - amount
。 - 第 16 行:當
new_size
小於當前容量的一半時,需要進行減容操作。 - 第 19 行:減容後的容量計算公式
new_capacity = max(4, new_size * 2)
,其中new_size * 2
可以證明是不會溢位的。 - 第 26~36 行:從末端元素進行收縮,且
copy
與dtor
函式都是trivial
型別的時候,只需要獲取mStorage
對應的SharedBuffer
進行 Resize 操作即可完成本次減容(以及收縮)。 - 第 37~54 行:一般情況下的減容步驟。
- 首先通過
SharedBuffer
申請一塊空間,大小為new_capacity * mItemSize
,讓指標array
指向其首地址。 - 將
mStorage
中第0 ~ (where - 1)
的元素拷貝到array
中。 - 若
where
小於new_size
,說明 Vector 後方還有一些元素需要拷貝過來。將mStorage
中從where + amount
處開始直至末尾的元素拷貝到當前array
最後一個元素的後面。 - 最後將當前
mStorage
指向的內容銷燬,然後在指向array
即完成了減容(以及收縮)操作。
- 首先通過
- 第 55~63 行:若判定不需要進行減容操作,則直接將
where
處開始的連續amount
個元素銷燬,再將where + amount
處開始直至 Vector 末尾的元素反向移動到where
處,就完成了一次收縮操作。
void VectorImpl::_shrink(size_t where, size_t amount)
{
if (!mStorage)
return;
// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
// this, (int)where, (int)amount, (int)mCount, (int)capacity());
ALOG_ASSERT(where + amount <= mCount,
"[%p] _shrink: where=%d, amount=%d, count=%d",
this, (int)where, (int)amount, (int)mCount); // caller already checked
size_t new_size;
LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount));
if (new_size < (capacity() / 2)) {
// NOTE: (new_size * 2) is safe because capacity didn't overflow and
// new_size < (capacity / 2)).
const size_t new_capacity = max(kMinVectorCapacity, new_size * 2);
// NOTE: (new_capacity * mItemSize), (where * mItemSize) and
// ((where + amount) * mItemSize) beyond this point are safe because
// we are always reducing the capacity of the underlying SharedBuffer.
// In other words, (old_capacity * mItemSize) did not overflow, and
// where < (where + amount) < new_capacity < old_capacity.
if ((where == new_size) &&
(mFlags & HAS_TRIVIAL_COPY) &&
(mFlags & HAS_TRIVIAL_DTOR))
{
const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
if (sb) {
mStorage = sb->data();
} else {
return;
}
} else {
SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
if (sb) {
void* array = sb->data();
if (where != 0) {
_do_copy(array, mStorage, where);
}
if (where != new_size) {
const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
_do_copy(dest, from, new_size - where);
}
release_storage();
mStorage = const_cast<void*>(array);
} else{
return;
}
}
} else {
void* array = editArrayImpl();
void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
_do_destroy(to, amount);
if (where != new_size) {
const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
_do_move_backward(to, from, new_size - where);
}
}
mCount = new_size;
}
小結
本文是 Vector 系列學習筆記的第一章,講述的是 Android Utils 中四種 Vector 的最基礎的部分,即抽象類 VectorImpl
。
稍作回顧,本文先從相關的標頭檔案入手,觀察 VectorImpl
的類定義,大概瞭解了這個類的一些基本功能,以及有哪些底層操作。隨後根據各個函式的實現,簡單介紹了類中成員函式的功能。最後,便是對三個我個人認為比較重要的函式 —— 排序、擴充,以及收縮函式,對它們進行了比較詳細的解析。
通過這一連串的分析,我對 VectorImpl
就有了一個比較全面的理解了。我認為在程式設計規範方面,我們內部需要向 google 學習的還是太多了。以及通過分析擴充與收縮函式(以及與之相關的一些函式),我發現 Vector 的一些基本操作都能通過複用它們來實現(實際上 google 就是這麼做的),比如插入元素、插入另一個 Vector、刪除元素……這使得整個類的實現的程式碼結構非常簡潔易懂,這種把基本操作高度抽象出來的做法值得我學習。
在分析過程中,我也發現還有一些更底層的內容我還沒有了解:
- 一個是
trivial
型別相關的內容 —— 這個型別的定義是什麼,它有什麼作用? - 另一個是關於
SharedBuffer
類 —— 它的設計思想是什麼,它通過怎樣的方式來實現基本操作的?
這些內容只能以後再慢慢學習了,而接下來需要分析的則是另一個重要的抽象類 SortedVectorImpl
。
相關文章
- Android學習筆記一Android筆記
- 《Android原始碼設計模式》學習筆記之ImageLoaderAndroid原始碼設計模式筆記
- Python學習筆記—程式碼Python筆記
- Python學習筆記|Python之程式Python筆記
- Android菜鳥學習js筆記一AndroidJS筆記
- Android 學習筆記雜記Android筆記
- Android學習筆記·ANRAndroid筆記
- Android學習筆記·HandlerAndroid筆記
- Android學習筆記·ADBAndroid筆記
- Android SQLite學習筆記AndroidSQLite筆記
- Android Linker學習筆記Android筆記
- Android Studio學習筆記Android筆記
- C++ 學習筆記(1):STL、Vector 與 SetC++筆記
- Android學習筆記14-從原始碼分析Toast的建立過程Android筆記原始碼AST
- Android學習筆記之build.gradle檔案Android筆記UIGradle
- 【學習筆記】初次學習斜率最佳化的程式碼及筆記筆記
- jQuery原始碼學習筆記一jQuery原始碼筆記
- Android 學習筆記核心篇Android筆記
- Android Gradle 學習筆記整理AndroidGradle筆記
- Android 學習筆記思考篇Android筆記
- 2018.03.06 Android Handler學習筆記Android筆記
- Android 開發學習筆記Android筆記
- 學習筆記之IdentityServer4(一)筆記IDEServer
- spring原始碼學習筆記之容器的基本實現(一)Spring原始碼筆記
- jQuery 學習筆記:jQuery 程式碼結構jQuery筆記
- JPG學習筆記2(附完整程式碼)筆記
- JPG學習筆記1(附完整程式碼)筆記
- JPG學習筆記3(附完整程式碼)筆記
- Android 學習筆記架構篇Android筆記架構
- React Native Android學習筆記 - 2015React NativeAndroid筆記
- 2018.03.16、Android-IntentService學習筆記AndroidIntent筆記
- Android環境搭建學習筆記Android筆記
- webpack 學習筆記:實戰之 babel 編碼Web筆記Babel
- 學習筆記(一)筆記
- Linux 學習筆記--程式Linux筆記
- 好程式設計師web前端培訓學習筆記Vue學習筆記一程式設計師Web前端筆記Vue
- Android原始碼學習之handlerAndroid原始碼
- 軒田機器學習技法課程學習筆記1 — Linear Support Vector Machine機器學習筆記Mac