前言:

首先,小匹夫要祝各位看官圣诞快乐,新年愉快~。上一篇文章《自己动手,实现一种类似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 = ; i < ; 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 = ; 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();
newItem.id = item.id * ;
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());
differentArray.Add(testArray.Get());
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 = ; 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 = ; 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 = ; 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 = , 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 = ; i < test.Length; i++)
{
Debug.Log (test[i].name);
}
} //默认顺序为NO. 0 ~ NO. 9
//乱序后,见图

好了,这周就到这里~小匹夫最近也在赶项目的途中,所以测试和修改的精力也被消耗了很多。过完元旦之后,再继续~

末了还是要说一声:各位元旦快乐~

完整的代码和测试可以在这里获取:https://github.com/chenjd/Unity3D_EggArray

装模作样的声明一下:本博文章若非特殊注明皆为原创,若需转载请保留原文链接及作者信息慕容小匹夫

自己动手,实现一种类似List<T>的数据结构(二)的更多相关文章

  1. 自己动手,实现一种类似List<T>的数据结构(一)

    前言 上一篇文章<Unity3D中常用的数据结构总结与分析>简单总结了一下小匹夫工作中经常遇到的一些数据结构.不过小匹夫一直有种观点,就是光说的热闹实际啥也不做真的没啥意思.光说不练假把式 ...

  2. jquery另外一种类似tab切换效果

    简要:最近做项目一些效果不能用淘宝kissy框架 所以代码得自己写啊 网上当然有很多组件 但是用他们的代码很多(有的是我不需要的代码) 且还要看API 还不如自己动手写个简单一个,是这么一种简单的效果 ...

  3. java讲讲几种常见的排序算法(二)

    java讲讲几种常见的排序算法(二) 目录 java讲讲几种常见的排序算法(一) java讲讲几种常见的排序算法(二) 堆排序 思路:构建一个小顶堆,小顶堆就是棵二叉树,他的左右孩子均大于他的根节点( ...

  4. SQL中一种类似GUID值的函数实现

        开发中会需要用到多列值组合成一个ID值的情况.比如做数据清洗的时候,一张表A有五列,分别是医院.科室.医生.职称.电话.面有许多重复的数据需要和另一个表B(和A列相同)做对比.清洗需要做两件事 ...

  5. 一种类似Retrofit声明接口即可实现调用的WebApi客户端框架

    为.Net出力 java有okhttp,还在okhttp这上搞了一个retrofit,.net有HttpClient,但目前我没有发现有类似的retrofit框架.最近在搞mqtt的webApi封装, ...

  6. React中ref的三种用法 可以用来获取表单中的值 这一种类似document.getXXId的方式

    import React, { Component } from "react" export default class MyInput extends Component { ...

  7. Apache是目前应用最广的Web服务器,PHP3是一种类似ASP的脚本语言

    一.如何获得软件? 获得这3个软件包的方法很多,目前大多数Linux分发都捆绑了这3个软件包,如RedHat.本文介绍的安装方法是基于从这些软件的官方站点上下载获得的软件包进行的,针对RedHat L ...

  8. Java的23种设计模式,详细讲解(二)

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  9. fiddler几种功能强大的用法(二)

    参考网址:http://blog.rekfan.com/articles/228.html http://www.cnblogs.com/tugenhua0707/p/4637771.html htt ...

随机推荐

  1. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  2. 在离线环境中使用.NET Core

    在离线环境中使用.NET Core 0x00 写在开始 很早开始就对.NET Core比较关注,一改微软之前给人的印象,变得轻量.开源.跨平台.最近打算试着在工作中使用.但工作是在与互联网完全隔离的网 ...

  3. js中参数不对应问题

    因为js是一种弱类型的编程语言,对数据类型的要求没有其他编程语言的要求严格,所以在定义函数的时候不需要像java和C#一样对其传入参数的类型进行定义.那么传入参数的个数有没有影响呢?今天小猪就做了个实 ...

  4. iOS热更新-8种实现方式

    一.JSPatch 热更新时,从服务器拉去js脚本.理论上可以修改和新建所有的模块,但是不建议这样做. 建议 用来做紧急的小需求和 修复严重的线上bug. 二.lua脚本 比如: wax.热更新时,从 ...

  5. x:bind不支持样式文件 或 此Xaml文件必须又代码隐藏类才能使用{x:Bind} 解决办法

    这两天学习UWP开发,发现一个很有趣的问题,就是我题目中的描述的. 我习惯了在ResourceDictionary中写样式文件,但是发现用x:Bind时会有问题 如果是写在Style里,则提示 “x: ...

  6. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  7. 源码分析netty服务器创建过程vs java nio服务器创建

    1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...

  8. vue双向数据绑定原理探究(附demo)

    昨天被导师叫去研究了一下vue的双向数据绑定原理...本来以为原理的东西都非常高深,没想到vue的双向绑定真的很好理解啊...自己动手写了一个. 传送门 双向绑定的思想 双向数据绑定的思想就是数据层与 ...

  9. Velocity初探小结--Velocity在spring中的配置和使用

    最近正在做的项目前端使用了Velocity进行View层的数据渲染,之前没有接触过,草草过了一遍,就上手开始写,现在又回头细致的看了一遍,做个笔记. velocity是一种基于java的模板引擎技术, ...

  10. ABP领域层

    1.实体Entites 1.1 概念 实体是DDD(领域驱动设计)的核心概念之一. 实体是具有唯一标识的ID且存储在数据库总.实体通常被映射成数据库中的一个表. 在ABP中,实体继承自Entity类. ...