• TStringList的Find,IndexOf和Sort


    procedure TForm1.Button1Click(Sender: TObject);
    var
      MyList: TStringList;
    begin
      MyList := TStringList.Create;
      try
        MyList.Add('11');
        MyList.Add('1');
        //经过测试可以知道,IndexOf是查询的是MyList中的每一项,而不单单是数据
        ShowMessage(IntToStr(MyList.IndexOf('1')));//结果为1
      finally
        MyList.Free;
      end;
    end;

    来自:http://www.cnblogs.com/monkeyking/articles/234685.html

    ----------------------------------------------------------------------------

    前几日工作很累,写代码时也有点心猿意马了,看到TStringList.Find便毫不犹豫地使用它在

    TStringList内部查找相关的数据。待调试代码时才知道痛苦,浪费无数时间后,只得一步步跟踪,才发

    现Find方法返回的Index总是错误的,当时一阵郁闷,随手按下F1键,Find的Help文档展现眼前,对于该

    函数是这样描述的:
    Locates the index for a string in a sorted list and indicates whether a string with that

    value already exists in the list.
    在Note部分又再次强调:Only use Find with sorted lists. For unsorted lists, use the IndexOf

    method instead.只怪自己一时懒惰,在不了解的情况下便抛弃习惯了的IndexOf,轻易使用新函数。但

    同时我也来了兴趣,为什么Find只能在使用TStringList.Sort方法后才能正常返回数据呢?

    老办法,直接跳到Classes文件中查看源代码:

    function TStringList.Find(const S: string; var Index: Integer): Boolean;
    var
      L, H, I, C: Integer;
    begin
      Result := False;
      L := 0;
      H := FCount - 1;
      while L <= H do
      begin
        I := (L + H) shr 1;
        C := CompareStrings(FList^[I].FString, S);
        if C < 0 then L := I + 1
        else begin
          H := I - 1;
          if C = 0 then
          begin
            Result := True;
            if Duplicates <> dupAccept then L := I;
          end;
        end;
      end;
      Index := L;
    end;

    还是被吓了一跳,怎么感觉这么复杂,仔细一看才明白,原来是个折半查找算法。呵呵。
    L,H变量分别代表Low和High,(L + H) shr 1就是求中间值的,完全等于(L + H) div 2,对于二进制,

    右移一位就相当于整除2。其中CompareStrings是用来对比两个字符串大小的:

    function TStringList.CompareStrings(const S1, S2: string): Integer;
    begin
      if CaseSensitive then
        Result := AnsiCompareStr(S1, S2)
      else
        Result := AnsiCompareText(S1, S2);
    end;

    这里的CaseSensitive用来标记是否大小写敏感,AnsiCompareStr是大小写敏感的,AnsiCompareText则

    反之。另外在Help文档中还特地说明了两个函数进行判断时,小写字符是小于大写字符的,比如'a'<'A'

    。请注意,这一点是与ASCII不相同的地方(如果再跟下去,你可以发现这两个函数是对API的一个封装,

    而且封装了Linux和Windows的两个版本)。

    此时我们返回到Find函数本身,又会发现在判断条件中只有C<0和C=0的情况,也就是说它只能搜索升序

    排列的StringList。

    忍不住,再看了看Sort方法。

    procedure TStringList.Sort;
    begin
      CustomSort(StringListCompareStrings);
    end;

    简单的不能再简单,一行语句。CustomSort是一个公共方法,供用户使用自定义的比较规则进行排序。

    StringListCompareStrings参数中放置的就是自定义比较规则的函数:

    TStringListSortCompare = function(List: TStringList; Index1, Index2: Integer): Integer;

    CustomSort的代码如下:

    procedure TStringList.CustomSort(Compare: TStringListSortCompare);
    begin
      if not Sorted and (FCount > 1) then
      begin
        Changing;
        QuickSort(0, FCount - 1, Compare);
        Changed;
      end;
    end;

    Changing和Changed主要是用来触发FOnChanging和FOnChanged的,具体内容可以自己看代码。而

    QuickSort则是使用快速排序算法和用户自定义的比较规则进行排序了,再跟入到QuickSort代码中:
    procedure TStringList.QuickSort(L, R: Integer; SCompare: TStringListSortCompare);
    var
      I, J, P: Integer;
    begin
      repeat
        I := L;
        J := R;
        P := (L + R) shr 1;
        repeat
          while SCompare(Self, I, P) < 0 do Inc(I);
          while SCompare(Self, J, P) > 0 do Dec(J);
          if I <= J then
          begin
            ExchangeItems(I, J);
            if P = I then
              P := J
            else if P = J then
              P := I;
            Inc(I);
            Dec(J);
          end;
        until I > J;
        if L < J then QuickSort(L, J, SCompare);
        L := I;
      until I >= R;
    end;

    哈哈,正是这一段

    while SCompare(Self, I, P) < 0 do Inc(I);
    while SCompare(Self, J, P) > 0 do Dec(J);

    使得TStringList是按照升序排列。至此,大致原因弄明白了。

    再看看IndexOf是如何实现搜索的,刚开始我认为它肯定是使用For循环遍历每个Item,遇到相同的内容

    则跳出循环,结果发现它确实也是这么做的,只是中间做了一些优化,假如StringList已经排序过,它

    会自动使用效率更高的Find方法进行查找,另外它使用Result作为循环变量,对资源的利用极其充分。

    代码如下:

    function TStringList.IndexOf(const S: string): Integer;
    begin
      if not Sorted then Result := inherited IndexOf(S) else
        if not Find(S, Result) then Result := -1;
    end;

    其中继承使用了父类TStrings中的IndexOf方法

    function TStrings.IndexOf(const S: string): Integer;
    begin
      for Result := 0 to GetCount - 1 do
        if CompareStrings(Get(Result), S) = 0 then Exit;
      Result := -1;
    end;

    这段代码中的Get方法在TStrings中则是纯虚函数

    function Get(Index: Integer): string; virtual; abstract;

    纯虚函数怎么能用,倒。那既然能用,只有一个可能,就是子类TStringList中实现了Get方法。返回到

    TStringList中,在果然看到以下代码:

    function TStringList.Get(Index: Integer): string;
    begin
      if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index);
      Result := FList^[Index].FString;
    end;

    他用来取得指定行的字符串。分析也就此结束。

  • 相关阅读:
    全能VIP音乐在线解析
    wordpress插件推荐
    day 34 IO模型
    day 33 协程、 socketserver模块
    day 32-2 练习
    day 32-2 并发编程——认识线程(下)
    day 32 练习
    day 32 并发编程——认识线程(上)
    day 31-1 练习
    day 31-1 守护进程、互斥锁、队列、生产消费模型
  • 原文地址:https://www.cnblogs.com/del88/p/3866436.html
Copyright © 2020-2023  润新知