自己動手,實現一種類似List 的數據結構(二)


前言:

首先,小匹夫要祝各位看官聖誕快樂,新年愉快~。上一篇文章《自己動手,實現一種類似List<T>的數據結構(一)》 介紹了一下不依靠List<T>實現的各種接口,仿造一個輕量級數據結構的過程。可能有的看官會有一些疑問,例如一些功能可以通過Linq提供的拓展來實現呀。此言不虛但也不全對,為了我們在工作中能方便的操作集合而提供的這些拓展方法(包括我們自己也可以構建的拓展方法),例如 Where,Any,Max,All...balalbala等等這些方法都是針對IEnumerable的對象進行擴展的,也就是說需要實現 IEnumerable接口。但是前面已經說了,小匹夫的用意是不實現各種List<T>繼承的接口。另外小匹夫的初衷是仿造和拓展 List<T>,將工作中需要使用到的各種功能集成到一個類中,所以有些現成的拓展方法不需要,有一些沒有的方法小匹夫也會自己實現一下(當然不是通過給現成的類添加拓展方法這種方式)。當然這篇文章介紹的東西還不成熟,需要慢慢完善,小匹夫也是把這個當做一個學習和實踐的機會。好啦,解釋完畢,那就介紹下今天的內容吧:

  1. 實現的方法的名稱和說明列
  2. 增加了3個委托來抽象3種情況。
  3. Map:通過委托把EggArray<T>中的每個值映射到一個新的EggArray<T>中
  4. Difference:返回的值來自EggArray<T>中,但同時不是傳入的Other里面的值
  5. Invoke:在EggArray<T>的每個元素上執行methodName方法。
  6. Pluck:萃取EggArray<T>中某字段值,返回一個數組,由於字段類型不確定,所以需要裝箱。
  7. 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

 

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:

EggArray<T>的每個元素上執行methodName方法。

/// <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

裝模作樣的聲明一下:本博文章若非特殊注明皆為原創,若需轉載請保留原文鏈接及作者信息慕容小匹夫


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM