- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
今天在编码的时候遇到了一个问题,需要对数组变量添加新元素和删除元素,因为数组是固定大小的,因此对新增和删除并不友好,但有时候又会用到,因此想针对数组封装两个扩展方法:新增元素与删除元素,并能到达以下三个目标:
1、性能优异; 。
2、兼容性好; 。
3、方便使用; 。
这三个目标最麻烦的应该就是性能优异了,比较后面两个可以通过泛型方法,扩展方法,按引用传递等语法实现,性能优异却要在十来种实现方法中选出两个最优的实现。那关于数组新增和删除元素你能想到多少种实现呢?下面我们来一起看看那个性能最好.
通过转为List,再用AddRange方法添加元素,最后再转为数组返回。代码实现如下:
public static int[] AddByList(int[] source, int[] added)
{
var list = source.ToList();
list.AddRange(added);
return list.ToArray();
}
因为数组实现了IEnumerable接口,所以可以直接调用Concat方法实现两个数组拼接。代码实现如下:
public static int[] AddByConcat(int[] source, int[] added)
{
return source.Concat(added).ToArray();
}
Array有个Copy静态方法可以实现把数组复制到目标数组中,因此我们可以先构建一个大数组,然后用Copy方法把两个数组都复制到大数组中。代码实现如下:
public static int[] AddByCopy(int[] source, int[] added)
{
var size = source.Length + added.Length;
var array = new int[size];
// 复制原数组
Array.Copy(source, array, source.Length);
// 添加新元素
Array.Copy(added, 0, array, source.Length, added.Length);
return array;
}
Span也有一个类似Array的Copy方法,功能也类似,就是CopyTo方法。代码实现如下:
public static int[] AddBySpan(int[] source, int[] added)
{
Span<int> sourceSpan = source;
Span<int> addedSpan = added;
Span<int> span = new int[source.Length + added.Length];
// 复制原数组
sourceSpan.CopyTo(span);
// 添加新元素
addedSpan.CopyTo(span.Slice(sourceSpan.Length));
return span.ToArray();
}
我想到了4种方法来实现,如果你有不同的方法希望可以给我留言,不吝赐教。那么那种方法效率最高呢?按我理解作为现在.net core性能中的一等公民Span应该性能是最好的.
我们也不瞎猜了,直接来一组基准测试对比。我们对4个方法,分三组测试,每组分别随机生成两个100、1000、10000个元素的数组,然后每组再进行10000次测试.
测试结果如下:
整体排名:AddByCopy > AddByConcat > AddBySpan > AddByList.
可以发现性能最好的竟然是Array的Copy方法,不但速度最优,而且内存使用方面也是最优的.
而我认为性能最好的Span整体表现还不如IEnumerable的Concat方法.
最终Array的Copy方法完胜.
还是先把数组转为List,然后再用RemoveAll进行删除,最后把结果转为数组返回。代码实现如下:
public static int[] RemoveByList(int[] source, int[] added)
{
var list = source.ToList();
list.RemoveAll(x => added.Contains(x));
return list.ToArray();
}
因为数组实现了IEnumerable接口,所以可以直接调用Where方法进行过滤。代码实现如下:
public static int[] RemoveByWhere(int[] source, int[] added)
{
return source.Where(x => !added.Contains(x)).ToArray();
}
Array有个FindAll静态方法可以实现根据条件查找数组。代码实现如下:
public static int[] RemoveByArray(int[] source, int[] added)
{
return Array.FindAll(source, x => !added.Contains(x));
}
直接遍历原数组,把满足条件的元素放入List中,然后转为数组返回。代码实现如下:
public static int[] RemoveByForList(int[] source, int[] added)
{
var list = new List<int>();
foreach (int item in source)
{
if (!added.Contains(item))
{
list.Add(item);
}
}
return list.ToArray();
}
还是直接遍历原数组,但是我们不创建新集合,直接把满足的元素放在原数组中,因为从原数组第一个元素迭代,如果元素满足则放入第一个元素其索引自动加1,如果不满足则等下一个满足的元素放入其索引保持不变,以此类推,直至所有元素处理完成,最后再把原数组中满足要求的数组复制到新数据中返回。代码实现如下:
public static int[] RemoveByForMarkCopy(int[] source, int[] added)
{
var idx = 0;
foreach (var item in source)
{
if (!added.Contains(item))
{
// 标记有效元素
source[idx++] = item;
}
}
// 创建新数组并复制有效元素
var array = new int[idx];
Array.Copy(source, array, idx);
return array;
}
这个方法和上一个方法实现基本一致,主要差别在最后一步,这个方法是直接通过Array的Resize静态方法把原数组调整为我们要的并返回。代码实现如下:
public static int[] RemoveByForMarkResize(int[] source, int[] added)
{
var idx = 0;
foreach (var item in source)
{
if (!added.Contains(item))
{
//标记有效元素
source[idx++] = item;
}
}
//调整数组大小
Array.Resize(ref source, idx);
return source;
}
同样的我们再做一组基准测试对比,结果如下:
可以发现最后两个方法随着数组元素增加性能越来越差,而其他四种方法相差不大。既然如此我们就选择Array原生方法FindAll.
新增删除的两个方法已经确定,我们第一个目标就解决了.
既然要封装为公共的方法,那么就必要要有良好的兼容性,我们示例虽然都是用的int类型数组,但是实际使用中不知道会碰到什么类型,因此最好方式是选择泛型方法。这样第二个目标就解决了.
那么第三个目标方便使用要怎么办呢?第一想法既然做成公共方法了,直接做一个帮助类,比如ArrayHelper,然后把两个实现方法直接以静态方法放进去.
但是我更偏向使用扩展方法,原因有二,其一可以利用编辑器直接智能提示出该方法,其二代码更简洁。形如下面两种形式,你更喜欢那种?
//扩展方法
var result = source.Add(added);
//静态帮助类方法
var result = ArrayHelper.Add(source, added);
现在还有一个问题,这个方法是以返回值的方式返回最后的结果呢?还是直接修改原数组呢?两种方式各有优点,返回新数组,则原数组不变便于链式调用也避免一些副作用,直接修改原数组内存效率高.
我们的两个方法是新增元素和删除元素,其语义更贴合对原始数据进行操作其结果也作用在自身。因此我更倾向无返回值的方式.
那现在有个尴尬的问题,不知道你还记得我们上一章节《C#|.net core 基础 - 值传递 vs 引用传递》讲的值传递和引用传递,这里就有个这样的问题,如果我们现在想用扩展方法并且无返回值直接修改原数组,那么需要对扩展方法第一个参数使用ref修饰符,但是扩展方法对此有限制要求【第一个参数必须是struct 或是被约束为结构的泛型类型】,显示泛型数组不满足这个限制。因此无法做到我心目中最理想的封装方式了,下面看看扩展方法和帮助类的代码实现,可以按需使用吧.
public static class ArrayExtensions
{
public static T[] AddRange<T>(this T[] source, T[] added)
{
var size = source.Length + added.Length;
var array = new T[size];
Array.Copy(source, array, source.Length);
Array.Copy(added, 0, array, source.Length, added.Length);
return array;
}
public static T[] RemoveAll<T>(this T[] source, Predicate<T> match)
{
return Array.FindAll(source, a => !match(a));
}
}
public static class ArrayHelper
{
public static void AddRange<T>(ref T[] source, T[] added)
{
var size = source.Length + added.Length;
var array = new T[size];
Array.Copy(source, array, source.Length);
Array.Copy(added, 0, array, source.Length, added.Length);
source = array;
}
public static void RemoveAll<T>(ref T[] source, Predicate<T> match)
{
source = Array.FindAll(source, a => !match(a));
}
}
注:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner 。
最后此篇关于C#|.netcore基础-扩展数组添加删除性能最好的方法的文章就讲到这里了,如果你想了解更多关于C#|.netcore基础-扩展数组添加删除性能最好的方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我知道如何通过iPhone开发创建sqlite数据库、向其中插入数据、删除行等,但我试图以编程方式删除整个数据库本身,但没有得到任何帮助。请有人指导我如何通过代码从设备中删除/删除整个 sqlite
请帮助指导如何在 Teradata 中删除数据库。 当我运行命令DROP DATABASE database_name时,我收到错误消息: *** Failure 3552 Cannot DROP d
Azure 警报规则的删除命令似乎不起作用,尝试了下面的方法,它返回状态为无内容,并且警报未被删除 使用的命令Remove-AzAlertRule -ResourceGroup "RGName"-Na
我在 flex 搜索中为大约50000个视频建立了索引,但是当它达到52000左右时,所有数据都被删除。嗯,这对我来说真的很奇怪,我没有为ES设置任何Heap大小或最小或最大大小的内存大小,因此它们没
我正在处理的问题是表单错误“输入由字母、数字、下划线或连字符组成的有效‘slug’。” 以下是我的表单字段验证: def clean_slug(self): slug = self.c
阅读文档,我希望 $("#wrap2").remove(".error") 从 中删除所有 .error 元素#wrap2。然而看看这个 JSFiddle: http://jsfiddle.net/h
嗨,我第一次尝试发现 laravel 我从 laravel 4.2 开始,我刚刚创建了一个新项目,但我误以为我写了这样的命令行 composer create-project laravel/lara
我已经在网上搜索了很长一段时间,但我找不到如何完全删除 apache 2.4 。 使用: Windows 7 c:\apache24\ 我已经尝试了所有命令,但没有任何效果。 httpd -k shu
可能是一个简单的答案,所以提前道歉(最少的编码经验)。 我正在尝试从任何列中删除具有特定字符串(经济 7)的任何行,并且一直在尝试离开此线程: How to drop rows from pandas
有几种方法可以删除/移除 vector 中的项目。 我有一个指针 vector ,我需要在类的析构函数中删除所有指针。 什么是最有效/最快甚至最安全的方式? // 1º std::for_each(v
我安装了一个 VNC 服务器并在某处阅读了我必须安装 xinetd 的信息。稍后我决定删除 VNC 服务器,所以我也删除了 xinetd。似乎 xinetd 删除了一些与 plesk 相关的文件,如果
我制作了一个从我们的服务器下载视频的应用。问题是: 当我取消下载时,我打电话: myAsyncTask.cancel(true) 我注意到,myAsyncTask 并没有在调用取消时停止...我的 P
是否可以在使用DELETE_MODEL删除模型之前检查模型是否存在我试图避免在尝试删除尚未创建的模型时收到错误消息。基本上我正在寻找对应的: DROP TABLE IF EXISTS 但对于模型。 最
我已经有了这个代码: 但它仍然会生成一个表行条目。 我想做的是,当输入的数量为0时,表行将被删除。请耐心等待,因为我是 php 和 mySQL 编码新手。 最佳答案 您忘记执行查询。应该是 $que
在 SharePoint 中,如果您删除/修改重复日历条目的单次出现,则不会真正删除/修改任何内容 - 相反,会创建一个新条目,告诉 SP 对于特定日期,该事件不存在或具有新参数. 因此,这可以通过删
在 routes.php 中我有以下路由: Route::post('dropzone', ['as' => 'dropzone.upload', 'uses' => 'AdminPhotoContr
在我的应用程序中,我正在尝试删除产品。当我第一次删除产品时,它会成功并且 URL 更改为/remove_category/15。我正在渲染到同一页面。现在,当我尝试删除另一个产品时,网址更改为/rem
这个问题被问了很多次,但给出的答案都是 GNU sed 特定的。 sed -i '' "/${FIND}/,+2d""$FILE" 给出“预期的上下文地址”错误。 有人可以给我一个例子,说明如何使用
在使用 V3 API 时,我找不到任何方法来删除和清理 Google map 。 我已经在 AJAX 站点中运行它,所以我想完全关闭它而无需重新加载页面。 我希望有一个 .unload() 或 .de
是否可以创建一个 Azure SQL 数据库用户来执行以下操作: 针对所有表和 View 进行 SELECT 创建/更改/删除 View 但用户不应该不拥有以下权限: 针对任何表或 View 插入/更
我是一名优秀的程序员,十分优秀!