C#에서 크기 N의 하위 목록으로 목록 분할

6618 단어
C#에서 목록[a, b, c, d, e, f, g, h]을 각각 세 개의 요소가 있는 하위 목록[a, b, c], [d, e, f], [g, h]과 세 개 미만인 하위 목록으로 분할하는 코드를 어떻게 작성합니까? 가장 인기 있는 답변은 다음 LINQ입니다.

List<List<T>> Split<T>(this IList<T> source, int length)
{
    return source
        .Select((x, i) => new { Index = i, Value = x })
        .GroupBy(x => x.Index / length)
        .Select(x => x.Select(v => v.Value).ToList())
        .ToList();
}


StackOverflow에서 이 LINQ는 두 가지 질문( Split a List into smaller lists of N size [duplicate] , Split List into Sublists with LINQ )에 대한 답변으로 900개 이상의 찬성 투표를 받았습니다.

그러나 이 방법은 가정된 답변에서 성능이 가장 나쁩니다. Index 및 Value를 소스의 길이로 사용하여 동일한 수의 객체를 생성합니다. 개체 생성은 메모리와 속도 측면에서 비용이 많이 드는 작업입니다. 비용은 훨씬 적지만 소스의 길이만큼 정확한 분할 및 비교 횟수도 성능에 좋지 않습니다.

성능이 필요하다면 애초에 LINQ를 사용하지 않는 것이 좋지만, 간결한 LINQ를 고집한다면 다음과 같은 해결 방법은 어떨까.

List<List<T>> Split<T>(this IList<T> source, int length)
{
    return Enumerable
        .Range(0, (source.Count + length - 1) / length)
        .Select(n => source.Skip(n * length).Take(length).ToList())
        .ToList();
}


다음은 1000 길이의 int 배열을 각각 세 조각으로 분할하는 BenchmarkDotNet에서 가져온 벤치마크 결과입니다. 첫 번째 답은 Splitter1이고 위는 Splitter2입니다. Mean은 평균이고 나머지는 측정 오류입니다. 결과는 Splitter2가 Splitter1보다 3.5배 이상 빠르다는 것을 보여줍니다.

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1766 (21H2)
AMD Ryzen 7 3800X, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.106
  [Host]  : .NET 6.0.6 (6.0.622.26707), X64 RyuJIT
  LongRun : .NET 6.0.6 (6.0.622.26707), X64 RyuJIT

Job=LongRun  IterationCount=100  LaunchCount=3  
WarmupCount=15  



방법
평균
오류
표준 편차


스플리터1
89.94μs
0.351μs
1.779μs

스플리터2
24.04μs
0.100μs
0.517μs


프로젝트의 대상 프레임워크가 .NET 6 이상인 경우 이 작업에 .NET 6에 도입된 Chunk 방법을 사용해야 합니다. Chunk 방법을 포함한 벤치마크 결과는 아래와 같습니다. Splitter2보다 4,000배 이상 빨랐습니다.


방법
평균
오류
표준 편차


스플리터1
88,650.102ns
245.2557ns
1,251.8625ns

스플리터2
23,481.503ns
117.8934ns
600.6975ns

큰 덩어리
5.609ns
0.0198ns
0.0984ns

좋은 웹페이지 즐겨찾기