前言:
首先,小匹夫要祝各位看官聖誕快樂,新年愉快~。上一篇文章《自己動手,實作一種類似List<T>的資料結構(一)》 介紹了一下不依靠List<T>實作的各種接口,仿造一個輕量級資料結構的過程。可能有的看官會有一些疑問,例如一些功能可以通過Linq提供的拓展來實作呀。此言不虛但也不全對,為了我們在工作中能友善的操作集合而提供的這些拓展方法(包括我們自己也可以建構的拓展方法),例如 Where,Any,Max,All...balalbala等等這些方法都是針對IEnumerable的對象進行擴充的,也就是說需要實作 IEnumerable接口。但是前面已經說了,小匹夫的用意是不實作各種List<T>繼承的接口。另外小匹夫的初衷是仿造和拓展 List<T>,将工作中需要使用到的各種功能內建到一個類中,是以有些現成的拓展方法不需要,有一些沒有的方法小匹夫也會自己實作一下(當然不是通過給現成的類添加拓展方法這種方式)。當然這篇文章介紹的東西還不成熟,需要慢慢完善,小匹夫也是把這個當做一個學習和實踐的機會。好啦,解釋完畢,那就介紹下今天的内容吧:
- 實作的方法的名稱和說明清單
- 增加了3個委托來抽象3種情況。
- Map:通過委托把EggArray<T>中的每個值映射到一個新的EggArray<T>中。
- Difference:傳回的值來自EggArray<T>中,但同時不是傳入的Other裡面的值
- Invoke:在EggArray<T>的每個元素上執行methodName方法。
- Pluck:萃取EggArray<T>中某字段值,傳回一個數組,由于字段類型不确定,是以需要裝箱。
- Shuffle:傳回一個随機亂序的T[]副本。
那麼下面我們就書接上文,繼續我們仿照和拓展List<T>的步伐。
Underscore.js的前緣
咦,這不是一篇關于Csharp的文章嗎?怎麼把JS給幹出來?哈哈,當然技術上并沒有什麼必然的關系,隻不過是小匹夫之前使用過cocos2d這套遊戲引擎開發過遊戲,有一段時間也很癡迷于cocos2d-js這種使用JS就能開發原生遊戲的能力。是以也接觸了一些js庫,對Underscore.js更是情有獨鐘。是以一提到要模仿List<T>這種内部其實是Array的資料結構,一個靈感就是為何不嘗試實作一些Underscore.js數組部分的若幹功能呢?是以下表EggArray<T>的新增方法中有部分借鑒于Underscore.js。
新增方法表
新增方法 | 說明 |
First | 傳回EggArray<T> 的第一個元素。傳遞 n參數将傳回數組中從第一個元素開始的n個元素 |
Last | 傳回EggArray<T> 的最後一個元素。傳遞 n參數将傳回數組中從最後一個元素開始的n個元素 |
Slice | 切割 |
Get | 預留 |
Set | |
AddFirst | 将對象添加到 EggArray<T> 的起始處。 |
RemoveLast | 從 EggArray<T> 中移除特定對象的最後一個比對項。 |
ContainsStrict | 确定某元素是否在 EggArray<T> 中。(嚴格判斷是否是同一個對象) |
IndexOfStrict | 搜尋指定的對象,并傳回整個 EggArray<T> 中第一個比對項的從零開始的索引。(同上) |
TryGet | 擷取指定類型對象 |
LastIndexOf | 搜尋指定的對象,并傳回整個 EggArray<T> 中第一個比對項的從結尾開始的索引。 |
Map | 通過委托把EggArray<T>中的每個值映射到一個新的EggArray<T>中 |
Filter | 周遊EggArray<T>中的每個值,傳回包含所有通過predicate真值檢測的元素值。 |
Without | 傳回一個删除所有values值後的 EggArray<T>副本。 |
Find | 在EggArray<T>中逐項查找,傳回第一個通過predicate疊代函數真值檢測的元素值 |
Every | 如果EggArray<T>中的所有元素都通過predicate的真值檢測就傳回true。 |
Some | 如果EggArray<T>中有任何一個元素通過 predicate 的真值檢測就傳回true。 |
Partition | 拆分一個EggArray<T>為兩個數組: 第一個數組其元素都滿足predicate疊代函數, 而第二個的所有元素均不能滿足predicate疊代函數 |
Difference | 傳回的值來自EggArray<T>中,但同時不是傳入的Other裡面的值 |
Uniq | 傳回 EggArray<T>去重後的副本 |
Invoke | 在EggArray<T>的每個元素上執行methodName方法。 |
Pluck | 萃取EggArray<T>中元素某屬性值,傳回一個數組。 |
Shuffle | 傳回一個随機亂序的T[]副本 |
Sample | 從 EggArray<T>中産生一個随機樣本。傳遞一個數字表示從EggArray<T>中傳回n個随機元素。否則将傳回一個單一的随機項。 |
各位看官可以看到,增加了許多挺有趣的功能。為了能将表中的功能名字變成真正的功能,我們還需要對上一篇文章中的變量&屬性部分做一些增改,如下我們增加了3個委托來抽象3種情況。
//定義三個委托來處理具體邏輯
public delegate void IterationHandler(T item);
public delegate bool IterationBoolHandler(T item);
public delegate T IterationVauleHandler(T item);
同時為了能測試我們的功能,我們還要定義一個用來被當做元素測試的類。
//被測試類
public class TargetClass
{
public int id;
public string name;
public TargetClass(int id)
{
this.id = id;
this.name = "NO. " + id;
}
public void Hi()
{
Debug.Log ("say hi");
}
}
同時還要有一個測試的環境,因為小匹夫是用mac做unity3d的開發,是以就直接使用unity3d的環境了。
/// <summary>
/// Egg array test.Based on Unity3D,各個元素的id為0-9
/// </summary>
using UnityEngine;
using System.Collections;
using EggToolkit;
public class EggArrayTest : MonoBehaviour {
EggArray<TargetClass> testArray = new EggArray<TargetClass>();
// Use this for initialization
void Start () {
for(int i = 0; i < 10; i++)
{
TargetClass test = new TargetClass(i);
testArray.Add(test);
}
// Test_Difference();
// Test_Invoke();
// Test_Pluck();
// Test_Shuffle();
// Test_Map();
}
void Update () {
}
}
下面就讓小匹夫帶領大家分析幾個具體的函數,并進行下測試吧。
Map:
使用了IterationVauleHandler這個委托,即需要傳回一個T類型的值。
//通過委托把EggArray<T>中的每個值映射到一個新的EggArray<T>中
public EggArray<T> Map(EggArray<T>.IterationVauleHandler handler)
{
EggArray<T> targetArray = new EggArray<T>(this.capacity);
for(int i = 0; i < this.count; i++)
{
T t = handler(this.items[i]);
targetArray.Add(t);
}
return targetArray;
}
在EggArrayTest中實作Test_Map這個方法:
void Test_Map()
{
EggArray<TargetClass> newArray = testArray.Map(delegate(TargetClass item) {
TargetClass newItem = new TargetClass(1);
newItem.id = item.id * 10;
return newItem;
});
newArray.Foreach(delegate(TargetClass item) {
Debug.Log (item.id);
});
}
//原元素的id為0-9,輸出為0,10,20...90
自己動手,實作一種類似List<T>的資料結構(二)

Difference:
調用了Filter方法,其中Filter方法的參數是一個IterationBoolHandler委托,即一個傳回bool值的委托。具體可以看Filter的實作。
/// <summary>
/// Difference the specified others.
///輸出不包含others中元素的EggArray<T>
/// </summary>
/// <param name="others">Others.</param>
public EggArray<T> Difference(EggArray<T> others)
{
EggArray<T> targetArray = new EggArray<T>();
targetArray =
this.Filter(delegate(T item) {
bool b = !others.Contains(item);
return b;
});
return targetArray;
}
在EggArrayTest中實作Test_Difference這個方法:
//作為參數傳入的EggArray<T>由testArray的第5,第9這2個元素組成
void Test_Difference()
{
EggArray<TargetClass> differentArray = new EggArray<TargetClass>();
differentArray.Add(testArray.Get(5));
differentArray.Add(testArray.Get(9));
testArray.Difference(differentArray).Foreach(delegate(TargetClass item) {
Debug.Log(item.name);
});
}
//輸出缺少no. 5,no. 9這兩個name
Invoke:
/// <summary>
/// Invoke the specified methodName.
/// 每個元素上執行methodName方法,若方法不存在則抛出exception
/// </summary>
/// <param name="methodName">Method name.</param>
public void Invoke(string methodName)
{
Type t = typeof(T);
var method = t.GetMethod(methodName);
if(method == null)
throw new Exception("沒有找到指定的方法哦~,可能不叫" + methodName);
for(int i = 0; i < this.count; i++)
{
method.Invoke(this.items[i], null);
}
}
在EggArrayTest中實作Test_Invoke這個方法:
//調用TargetClass的HI()方法
void Test_Invoke()
{
testArray.Invoke("Hi");
}
//輸出:say hi
Pluck:
萃取EggArray<T>中某字段值,傳回一個數組,由于字段類型不确定,是以需要裝箱。當傳入的名稱無法查找到該字段時,抛出exception。
/// <summary>
/// Pluck the specified fieldName.
/// 萃取某字段值,傳回一個數組
/// 由于字段類型不确定,是以需要裝箱
/// </summary>
/// <param name="fieldName">Field name.</param>
public object[] Pluck(string fieldName)
{
Type t = typeof(T);
object[] targetArray = new object[this.count];
var field = t.GetField(fieldName);
if(field == null)
throw new Exception("沒有找到指定的field哦~,可能不叫" + fieldName);
for(int i = 0; i < this.count; i++)
{
object value = field.GetValue(this.items[i]);
targetArray[i] = value;
}
return targetArray;
}
在EggArrayTest中實作Test_Pluck這個方法:
//擷取各個元素 字段id的值
void Test_Pluck()
{
object[] testObj = testArray.Pluck("id");
string testString = string.Empty;
for(int i = 0; i < testObj.Length; i++)
{
testString = testObj[i].ToString();
Debug.Log ("field value is " + testString);
}
}
//輸出為0-9
Shuffle:
傳回一個随機亂序的T[],下面看代碼
/// <summary>
/// Shuffle this instance.
/// 傳回一個随機亂序的副本
/// </summary>
public T[] Shuffle()
{
T[] shuffled = new T[this.count];
Random random = new Random();
for (int index = 0, rand; index < this.count; index++) {
rand = random.Next(index);
if (rand != index)
shuffled[index] = shuffled[rand];
shuffled[rand] = this.items[index];
}
return shuffled;
}
在EggArrayTest中實作Test_Shuffle這個方法:
//
void Test_Shuffle()
{
TargetClass[] test = testArray.Shuffle();
for(int i = 0; i < test.Length; i++)
{
Debug.Log (test[i].name);
}
}
//預設順序為NO. 0 ~ NO. 9
//亂序後,見圖
好了,這周就到這裡~小匹夫最近也在趕項目的途中,是以測試和修改的精力也被消耗了很多。過完元旦之後,再繼續~
末了還是要說一聲:各位元旦快樂~
完整的代碼和測試可以在這裡擷取:https://github.com/chenjd/Unity3D_EggArray
裝模作樣的聲明一下:本博文章若非特殊注明皆為原創,若需轉載請保留原文連結及作者資訊慕容小匹夫
本作品采用知識共享署名-非商業性使用-相同方式共享 2.5 中國大陸許可協定進行許可,我的部落格歡迎複制共享,但在同時,希望保留我的署名權陳嘉棟(慕容小匹夫),并且,不得用于商業用途。如您有任何疑問或者授權方面的協商,請給我留言。
知乎專欄:
Runtime
聯系方式:
Email:[email protected]