.Net中的集合排序可以這么玩你知道嗎
C#集合類型概述
集合是.NET FCL(Framework Class Library)中很重要的一部分。所有的集合類都繼承自IEnumerable。集合類總體可分為一下幾類:關(guān)聯(lián)/非關(guān)聯(lián)型集合,順序/隨機(jī)訪問(wèn)集合,順序/無(wú)序集合,泛型/非泛型集合,線程安全集合。
各集合類底層接口關(guān)系圖

背景:
public class StockQuantity
{
public StockQuantity(string status, DateTime dateTime, int quantity)
{
Status = status;
DateTime = dateTime;
Quantity = quantity;
}
public string Status { get; set; }
public DateTime DateTime { get; set; }
public int Quantity { get; set; }
}
該對(duì)象,主要有三個(gè)字段,現(xiàn)在的業(yè)務(wù)需求是,取到了一個(gè)類型為L(zhǎng)ist<StockQuantity>集合StockQuantities,需要對(duì)該集合進(jìn)行三次排序,排序規(guī)則及優(yōu)先級(jí)如下:
1. Status為空的排在后面,不為空的排在前面,不關(guān)心Status的內(nèi)容,只關(guān)心Status是否為空。
2. DateTime升序排序。
3. Quantity升序排序。
小白我的做法:
我只知道可以對(duì)集合用OderBy排序,對(duì)以上三條規(guī)則,所以設(shè)計(jì)思路如下。
1. StockQuantities.OrderBy(u=>u.Status)
錯(cuò)誤,
該排序得規(guī)則不僅僅會(huì)考慮Status是否為空,還會(huì)考慮Status的內(nèi)容。
如果Status是[“b”,”c”,null,”d”] ,那么排序結(jié)果是[null,“b”,”c”,”d”] 。
而我們要的結(jié)果是[“b”,”c”,”d” ,null] (直接把null的丟到最后,別的不動(dòng))
怎么辦?
暫時(shí)不知道,先不管
2. 對(duì)DateTime進(jìn)行升序排序,這簡(jiǎn)單
StockQuantities.OrderBy(u=>u.DateTime)
半對(duì)!
為什么半對(duì),看下面
3. 在排序2的前提下,用OrderBy,也就是StockQuantities.OrderBy(u=>u.DateTime).OrderBy(u=>u.Quantity)
錯(cuò)誤!
以上表達(dá)式等同于下面兩條的表達(dá)式:
StockQuantities = StockQuantities.OrderBy(u=>u.DateTime) StockQuantities = StockQuantities.OrderBy(u=>u.Quantity)
所以第一條代碼就是廢代碼,最終排序還是以Quantity進(jìn)行排序的。
雖然我是小白,但我還是明白這樣是錯(cuò)誤的,所以我的做法是
stockQuantities = stockQuantities.OrderBy(u => u.DateTime).ToList();
foreach (var dateOrder in stockQuantities)
{
var datetimeOrderBy = stockQuantities.Where(u => u.DateTime.Date == dateOrder.DateTime.Date) .OrderBy(u => u.Count);
foreach (var countOrder in datetimeOrderBy)
{
if (countOrder.OutPut == false)
{
Console.WriteLine($"{countOrder.Status}-{countOrder.DateTime}-{countOrder.Count}");
countOrder.OutPut = true;
}
}
}
Console.ReadKey();
采用雙層循環(huán),先取到按時(shí)間排序的數(shù)據(jù) dateOrder,再去和該條數(shù)據(jù)在同一天的所有數(shù)據(jù)并對(duì)Quantity進(jìn)行排序,為了防止重復(fù)的輸出,我同時(shí)給StockQuantity對(duì)象加上了Output屬性,當(dāng)該屬性為false為,則輸出該對(duì)象的內(nèi)容,并把Output屬性設(shè)為true,這樣就不會(huì)重復(fù)輸出了,而且實(shí)現(xiàn)了先對(duì)DateTime排序,再對(duì)Quantity進(jìn)行排序。
So Easy!!
然而,當(dāng)開(kāi)心地把這樣的代碼提交之后,卻被同事狠狠地鄙視了,說(shuō)到:“什么爛代碼?。 比坏肋€有比這更好的代碼?
給同事倒了一杯茶,點(diǎn)了一根煙,虛心請(qǐng)教。
大佬做法:
同事給我講了兩招,分別是條件排序、多級(jí)排序。
什么是條件排序,怎么用?
1. StockQuantities.OrderBy(u=>u.Status==null)
這就是條件排序,可是咋一看,給人一種是把Status為空的排前面,不為空的排后面的錯(cuò)覺(jué)。
其實(shí)不然,我們看到OrderBy里面的一個(gè)返回值為bool類型的表達(dá)式,該排序先排結(jié)果為0(false)的,再排結(jié)果為1(true)的。這種排序只考慮返回的bool值,不考慮參數(shù)的具體值,所以姑且稱它為條件排序。
完全符合排序規(guī)則1的要求。
什么是多級(jí)排序,怎么用?
2. 利用我上面我的代碼排序雖然可以實(shí)現(xiàn)先排DateTime,再排Quantity,但是該算法的時(shí)間復(fù)雜度的n*n,而且給StockQuantity添加了output字段,明顯是不科學(xué)的。
然而,連續(xù)地使用多個(gè)OrderBy最終只會(huì)生效最后一個(gè)OrderBy,天無(wú)絕人之路,所以這個(gè)時(shí)候應(yīng)該使用ThenBy??!
使用ThenBy可以講以上的三條排序規(guī)則簡(jiǎn)化如下:
stockQuantities = stockQuantities.OrderBy(u => u.Status==null).ThenBy(u => u.DateTime).ThenBy(u => u.Quantity).ToList();
即可完美地實(shí)現(xiàn)再前一個(gè)排序前提下進(jìn)行二級(jí)排序。
優(yōu)化后的完整代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
namespace OrderBy
{
class Program
{
static void Main(string[] args)
{
var stockQuantities = new List<StockQuantity>()
{
new StockQuantity("正常品",new DateTime(2017,4,16),12 ),
new StockQuantity("正常品",new DateTime(2017,4,17),15 ),
new StockQuantity("殘次品",new DateTime(2017,4,16),10 ),
new StockQuantity("殘次品",new DateTime(2017,4,17),8 ),
new StockQuantity(null,new DateTime(2017,4,18),8 ),
};
stockQuantities = stockQuantities.OrderBy(u => u.Status==null).ThenBy(u => u.DateTime).ThenBy(u => u.Quantity).ToList();
foreach (var stockQuantity in stockQuantities)
{
Console.WriteLine($"{stockQuantity.Status}-{stockQuantity.DateTime}-{stockQuantity.Quantity}");
}
Console.ReadKey();
}
}
public class StockQuantity
{
public StockQuantity(string status, DateTime dateTime, int quantity)
{
Status = status;
DateTime = dateTime;
Quantity = quantity;
}
public string Status { get; set; }
public DateTime DateTime { get; set; }
public int Quantity { get; set; }
}
}
簡(jiǎn)單的一個(gè)排序優(yōu)化,就把程序的時(shí)間復(fù)雜度從N*N降低到了N,所以在這里把這兩種排序技巧分享出來(lái),希望對(duì)不會(huì)的同學(xué)有所幫助。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
ASP.NET Core MVC通過(guò)IViewLocationExpander擴(kuò)展視圖搜索路徑的實(shí)現(xiàn)
這篇文章主要介紹了ASP.NET Core MVC通過(guò)IViewLocationExpander擴(kuò)展視圖搜索路徑的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
C#默認(rèn)以管理員身份運(yùn)行程序?qū)崿F(xiàn)代碼
權(quán)限不夠,導(dǎo)致無(wú)法修改系統(tǒng)時(shí)間,于是我以管理員身份運(yùn)行了一次,結(jié)果測(cè)試成功,下面為大家介紹下C#如何默認(rèn)以管理員身份運(yùn)行程序2014-03-03
如此高效通用的分頁(yè)存儲(chǔ)過(guò)程是帶有sql注入漏洞的zt
通常大家都會(huì)認(rèn)為存儲(chǔ)過(guò)程可以避免sql注入的漏洞,這適用于一般的存儲(chǔ)過(guò)程,而對(duì)于通用分頁(yè)存儲(chǔ)過(guò)程是不適合的,請(qǐng)看下面的代碼和分析!2010-07-07
Gridview利用DataFormatString屬性設(shè)置數(shù)據(jù)格式的方法
這篇文章主要介紹了Gridview利用DataFormatString屬性設(shè)置數(shù)據(jù)格式的方法,涉及Gridview屬性操作及數(shù)據(jù)格式相關(guān)技巧,需要的朋友可以參考下2016-03-03
.Net極限生產(chǎn)力之分表分庫(kù)全自動(dòng)化Migrations?Code-First
這篇文章主要介紹了.Net極限生產(chǎn)力之分表分庫(kù)全自動(dòng)化Migrations?Code-First,輕量級(jí)針對(duì)分表分庫(kù)讀寫(xiě)分離的解決方案,具有零依賴、零學(xué)習(xí)成本、零業(yè)務(wù)代碼入侵適配2022-07-07
asp.net?Core中同名服務(wù)注冊(cè)的實(shí)現(xiàn)代碼
Asp.Net?Core中自帶了容器,同時(shí)也可以使用AutoFac替換掉默認(rèn)的容器,以下為兩種方式同名服務(wù)的注冊(cè)實(shí)現(xiàn),對(duì)asp.net?Core服務(wù)注冊(cè)的實(shí)現(xiàn)代碼感興趣的朋友一起看看吧2022-03-03
asp.net登錄驗(yàn)證碼實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了asp.net登錄驗(yàn)證碼實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08

