2010-06-16 4 views
9

이전 3.5 이전 프레임 워크의 레거시 코드를 통해 다시 실행되는 것을 발견하고 전체 동기화 된 방식으로 업데이트해야하는 목록과 사전 목록 나는 이러한 프로세스를 새로운 사용자 정의 클래스의 사용자 정의 컨테이너 클래스로 수렴함으로써 활용하고 이해하기가 훨씬 더 쉽다는 결론을 내 렸습니다. 그러나 특정 내부 속성에 따라 새 컨테이너 클래스의 내용을 구성하는 것에 대한 우려가 생겼다는 몇 가지 점이 있습니다. 예를 들어 한 클래스의 ID 번호 속성을 기준으로 정렬합니다.목록 유틸리티 <T> .Sort() 대 List <T> .OrderBy() 사용자 지정 컨테이너 클래스의 멤버

컨테이너 클래스는 주로 일반 List 개체를 기반으로하므로 내 첫 번째 본능은 IComparable을 사용하여 내부 클래스를 작성하고 속성을 비교하는 CompareTo 메서드를 작성하는 것이 었습니다. 이렇게하면 정렬을 호출 할 때 items.Sort()으로 전화 할 수 있습니다.

그러나 대신 items = items.OrderBy(Func)을 대신 사용하려고 생각했습니다. 이렇게하면 다른 속성으로 정렬해야하는 경우보다 유연 해집니다. 정렬에 사용 된 속성은 IComparable 코드를 조회하지 않고 정렬 호출과 일렬로 나열되므로 가독성이 더 좋습니다. 결과적으로 전반적인 구현은보다 깔끔하게 느껴집니다.

조숙 한 또는 마이크로 최적화는 신경 쓰지 않지만 일관성을 좋아합니다. 적절한 경우에 한 가지 종류의 구현을 고수하고 필요한 곳마다 다른 구현을 사용하는 것이 가장 좋습니다. List.Sort를 사용하는 대신 LINQ OrderBy를 사용하도록 코드를 변환 할 가치가 있습니까? 이러한 사용자 지정 컨테이너에 대한 IComparable 구현을 고수하는 것이 더 나은 방법입니까? 어느 경로에서든 중요한 기계적 이점이 제공되어 결정을 저울질해야합니까? 아니면 최종 기능이 코더의 기본 설정이되는 지점과 동일합니까?

답변

18

여기서 중요한 점은 List<T>.Sort()이 정렬을 수행한다는 것입니다. 목록이 외부 코드에 노출되어 있으면이 코드에 항상 같은 개체가 표시됩니다. 목록이 컨테이너 클래스 외부의 코드에 의해 필드에 유지되는 경우 중요합니다. OrderBy()으로 정렬하는 경우 이전에 items을 대체하여 매번 새로운 목록이 생성됩니다. 이전에 저장된 목록은 수업의 현재 상태를 나타내지 않습니다.

성능을 고려하면 OrderBy은 항목을 정렬하기 위해 전체 목록을 반복해야합니다. 그런 다음 ToList()으로 호출하여이 목록에서 새 목록을 만들고 두 번째 목록을 반복합니다. 게다가 목록이므로 열거 형은 배가 알고리즘을 사용하여 모든 요소가 들어갈 때까지 크기를 늘립니다. 큰 목록의 경우에는 할당 및 메모리 복사가 상당히 많을 수 있습니다. 나는 성능이 List<T>.Sort()보다 훨씬 나쁠 것이라고 기대한다.

편집 : 작은 벤치 마크 :

internal class Program { 

    private static List<int> CreateList(int size) { 

     // use the same seed so that every list has the same elements 
     Random random = new Random(589134554); 

     List<int> list = new List<int>(size); 
     for (int i = 0; i < size; ++i) 
      list.Add(random.Next()); 
     return list; 
    } 

    private static void Benchmark(int size, bool output = true) { 
     List<int> list1 = CreateList(size); 
     List<int> list2 = CreateList(size); 

     Stopwatch stopwatch = Stopwatch.StartNew(); 
     list1.Sort(); 
     stopwatch.Stop(); 
     double elapsedSort = stopwatch.Elapsed.TotalMilliseconds; 
     if (output) 
      Console.WriteLine("List({0}).Sort(): {1}ms (100%)", size, elapsedSort); 

     stopwatch.Restart(); 
     list2.OrderBy(i => i).ToList(); 
     stopwatch.Stop(); 
     double elapsedOrderBy = stopwatch.Elapsed.TotalMilliseconds; 
     if (output) 
      Console.WriteLine("List({0}).OrderBy(): {1}ms ({2:.00%})", size, elapsedOrderBy, elapsedOrderBy/elapsedSort); 

    } 

    internal static void Main() { 

     // ensure linq library is loaded and initialized 
     Benchmark(1000, false); 

     Benchmark(10); 
     Benchmark(100); 
     Benchmark(1000); 
     Benchmark(10000); 
     Benchmark(100000); 
     Benchmark(1000000); 

     Console.ReadKey(); 
    } 
} 

출력 (List.Sort 정규화) : 당신이 언급 한 이후

정렬
List(10).Sort(): 0,0025ms (100%) 
List(10).OrderBy(): 0,0157ms (628,00%) 
List(100).Sort(): 0,0068ms (100%) 
List(100).OrderBy(): 0,0294ms (432,35%) 
List(1000).Sort(): 0,0758ms (100%) 
List(1000).OrderBy(): 0,3107ms (409,89%) 
List(10000).Sort(): 0,8969ms (100%) 
List(10000).OrderBy(): 4,0751ms (454,35%) 
List(100000).Sort(): 10,8541ms (100%) 
List(100000).OrderBy(): 50,3497ms (463,88%) 
List(1000000).Sort(): 124,1001ms (100%) 
List(1000000).OrderBy(): 705,0707ms (568,15%) 
+0

글쎄, 벤치 마크에 표시된 볼륨은 볼륨을 말합니다.그런 속도로 3 개의 OrderBys를 사용하여 한 점을 대체하기 위해 3 가지 종류로 짜낼 수 있으며 하나의 OrderBy를 사용한 것보다 더 나은 성능을 얻을 수 있습니다! Sort()입니다! –

+0

당신의 계산에 의해 판단되는 빠른 후속 조치는 ... 이것은 items.First()가'items [0]'보다 상당히 느리다는 것을 의미합니까? –

+0

아니요,'First()'는'IList '구현에 최적화되어 있으며'list [0]'을 반환합니다. 나는 성과가 같을 것을 기대한다. 열거 형을 직접 사용한다고 할지라도, 여전히 반복 최적화는 단 하나의 항목이므로 마이크로 최적화라고 할 수 있습니다. –

4

줄리앙 말했듯이,() 아마 그러나 빨라집니다 SortBy()의 가독성과 유연성이 뛰어날 때 Sort() 메서드로도이를 수행 할 수 있습니다 (최소한 소트를 기반으로하는 Property가 비교 가능할 경우)

items = items.OrderBy(x => x.Name).ToList(); 
items.Sort((x,y) => x.Name.CompareTo(y.Name)); // If x.Name is never null 
items.Sort((x,y) => String.Compare(x.Name, y.Name)); // null safe 
+0

요즘에는 "IComparable에 의존하는 대신 Sort 함수를 지정할 수 있습니다"와 같은 작은 것들을 잊어 버릴 것입니다. 어느 것이 좋습니다. 특정 클래스가 실제로 그렇게 느껴지지 않을 때 "비교 가능"하다고 지정하는 것을 피할 수 있기 때문입니다. 특정 상황에서 정렬 될 필요가 있습니다. Julien의 데이터를 바탕으로 귀하의 답변을 결합하여 성공을 거두었습니다. 많은 감사합니다! –

+0

나는 IComparer에 IComparer 를 선호합니다. 그렇게하면 정렬 알고리즘의 다른 캡슐화를 전달할 수 있습니다. –

관련 문제