- PR -

ArrayList.IndexOfの使用方法について

投稿者投稿内容
アキミ
常連さん
会議室デビュー日: 2003/05/30
投稿数: 21
投稿日時: 2003-10-11 21:50
お世話になっております。
アキミです。

独自のクラスを作成して、そのクラスをArrayListによって
配列としました。

コード:

Class TableField

Plivate Field As String
Plivate Text as string

Sub New(ByVal _field As String, ByVal _text As String)

    Me.Field = _field
    Me.Text  = _text

End Sub

End Class



このクラスに対して
コード:

Dim Record As New ArrayList

For i As Integer = 0 To myReader.FieldCount() - 1 
    Record.Add(new TableField(myReader.GetName(i), myReader.GetString(i))
Next


というように、データベースから取得したレコードを入れています。

このRecord(ArrayList)に対して、「Textが"ShopName"というTableFieldのインデックスを取得する」という方法でindexOfを使用したいのですが、その方法がわかりません。
テストとしてRecord.indexOf(new TableField("ShopName","7-11"))という方法をしてみても「-1」が入ってきてしまいます。

とりあえず、今は作成したTableFieldクラスを使用せずに、
コード:

Me.Field.Add(myReader.GetName(i))
Me.Text.Add(myReader.Item(i))


といった感じでデータを追加して
コード:

Me.Text.Item(Me.Field.IndexOf("ShopName"))


という風に取得しています。

前述のような、作成したクラスで検索が出来れば美しそうな感じなんですが。
なにかいい方法はないでしょうか?
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2003-10-11 22:21
こんにちわ。諸農です。

ちゃんと確認していませんが、TableFieldクラスのToString()メソッドを
オーバーライドして、どちらかのフィールドに格納されている文字列を
返すようにしてみてはいかがでしょうか。


ではでは(^^)/

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2003-10-11 22:50
引用:

アキミさんの書き込み (2003-10-11 21:50) より:
このRecord(ArrayList)に対して、「Textが"ShopName"というTableFieldのインデックスを取得する」という方法でindexOfを使用したいのですが、その方法がわかりません。
テストとしてRecord.indexOf(new TableField("ShopName","7-11"))という方法をしてみても「-1」が入ってきてしまいます。
--
前述のような、作成したクラスで検索が出来れば美しそうな感じなんですが。
なにかいい方法はないでしょうか?


ArrayListに格納するクラス(ここではTableFieldクラス)で、Equalsメソッドをオーバーライドする事で、比較方法をカスタマイズできます。

※ArrayList.IndexOfのドキュメントより
このメソッドは、 Object.Equals を呼び出して、等しいかどうかを判断します。

ただし、Textフィールドが等しい場合に同じオブジェクトとみなしてよい場合にしか使えません。
調べてみましたが、ArrayListの検索で比較方法を自由に指定出来るものはなさそうですね。
# BinarySearchならIComaparerを指定出来るようですが、
# この場合は先にソートが必要になります。

追加順とインデックスが重要なのであれば仕方ないですが、そうでなければディクショナリ系のものを使う方がシンプルかもしれません。
# もしくは、上述のようにソートしてBinarySearchを使うか。
ya
大ベテラン
会議室デビュー日: 2002/05/03
投稿数: 212
投稿日時: 2003-10-11 23:21
参照の等価性と値の等価性の違いです。

http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/dnguinet/htm/drguinet01312001.asp
この辺の細かい話は、↑に書いてありますので、参照してください(問題の箇所は一部分に埋もれてます…Equalsのところ)。

引用:

このRecord(ArrayList)に対して、「Textが"ShopName"というTableFieldのインデックスを取得する」という方法でindexOfを使用したいのですが、その方法がわかりません。
テストとしてRecord.indexOf(new TableField("ShopName","7-11"))という方法をしてみても「-1」が入ってきてしまいます。



この場合、classのデフォルトでの Equals() メソッドは参照の等価性を利用しますので、新しく作ったオブジェクトはたとえ値が同じでも別のものと判断されます。別のものなので、「ArrayList.IndexOf メソッドは格納されていないと判断」→「-1がかえってくる」わけです。

そのクラスを値の等価性において等価とみなすようにするためには Equals() メソッドをオーバーライドします。ただ、これはかなりコツがいる作業になるので(説明するのは骨)、参考となるコードを挙げておきますね(C#ですみませぬ)。

コード:

public class Test
{
  private string value="";
  public string Value
  {
    get
    {
      return this.value;
    }
    set
    {
      this.value = value;
    }
  }

  public Test(){}

  public override bool Equals(object obj)
  {
    Test t = obj as Test;
    if( object.ReferenceEquals(t, null) )return false;
    else return this.value == t.value;
  }
  public override int GetHashCode()
  {
    return value.GetHashCode();
  }

  public static bool operator==(Test a, Test b)
  {
    if( object.ReferenceEquals(a, null) )
    {
      if( object.ReferenceEquals(b, null) )return true;
      else return false;
    }
    else
    {
      if( object.ReferenceEquals(b, null) )return false;
      else return a.value == b.value;
    }
  }
  public static bool operator!=(Test a, Test b)
  {
    return !(a==b);
  }
}



…かなり適当にかいたので問題があったら誰か指摘してください。

ちなみに、めんどくさい/わからないのならデータを保持する(ArrayListに格納する)classをstructにしてみてください。何も考えずに ArrayList.IndexOf メソッドが期待通りの動作をすると思います。
アキミ
常連さん
会議室デビュー日: 2003/05/30
投稿数: 21
投稿日時: 2003-10-12 05:04
Jubeiさん、なちゃさん、yaさん、ありがとうございます。

参考のリンクとご説明により、できない理由が見えてきました。
値型と参照型は理解しているつもりで、まだまだ全然だめなようです…。

Equalsをオーバーライドするのが一番素敵な方法だと思うのですが、
今現在はその様な力もないのが現状でして、それはもう少しレベルアップ
したときのお楽しみとして取っておこうと思います。

yaさんのあげて頂いた、もう一つの方法である
「Structure」での処理をやってみようと思いました。

DBFieldクラスを
コード:

Structure DBField

    Public Field As String
    Public Text As String

End Structure

の用に作り替えて実行してみました。

ところが
コード:

Dim mydbf as DBField
mydbf.Field = "ShopName"

Record.indexOf(mydbf)

では検索できず
コード:

Dim mydbf as DBField
mydbf.Field = "ShopName"
mydbf.Text = "7-11"

Record.indexOf(mydbf)

という方法でしか検索できませんでした。
考えてみれば、「TextがNothing」なわけで、一致しないというのは正しい動きだと思います。

toStringをオーバーライドしてみたのですが、結果は変わりません。
コード:
Structure DBField

    Public Field As String
    Public Text As String

    Overrides Function toString() As String

        Return Me.Field

    End Function

End Structure


「なにか」をオーバーライドすれば出来る…ような気もするのですが。
その「なにか」が解らない次第です。
勉強が足りないのはわかっているのですが、お知恵を拝借させていただければ幸いです。
よろしくお願い致します。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2003-10-12 09:48
引用:

アキミさんの書き込み (2003-10-12 05:04) より:

「なにか」をオーバーライドすれば出来る…ような気もするのですが。
その「なにか」が解らない次第です。


何かをオーバーライドする事による方法は、Equalsをオーバーライドしかないと思います(ArrayListのIndexOfの動作より)。

Structにした場合に何が起こるかというと、デフォルトのEquals実装が、オブジェクトの参照の比較ではなく、各メンバのEquals呼び出しによる実装に変わるということだったと思います。
# 微妙に間違ってるかもしれませんが、そういうイメージです。

ですので、自分で作成した構造体では、クラスと違い、Equalsをオーバーライドしなくても自動的にフィールドの値による比較が出来る事になります。
# 多分リフレクションで呼び出すとかの可能性があるので、
# パフォーマンスは悪いかもしれません。

今回のように、特定のフィールドの値で比較する必要がある場合には、やっぱりEqualsをオーバーライドする必要があります(それが問題ないかともかくとして)。

あと、ArrayListなどのコレクションに格納するのが前提のオブジェクトは、構造体にはしない方が良いかもしれません。
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2003-10-12 14:48
こんにちわ。諸農です。

引用:

アキミさんの書き込み (2003-10-12 05:04) より:

toStringをオーバーライドしてみたのですが、結果は変わりません。



申し訳ないです。既に皆さんからのRESにもあるように、
実装はEquals()メソッドをオーバーライドするのが正しいと思います。

v1.1のSDKのArrayList.IndexOf()の説明にはハッキリと、
引用:

このメソッドは、 Object.Equals を呼び出して、等しいかどうかを判断します。


と書かれていました。

yaさんから提示されているコードは、かなり参考になると思います。

その他にも、C#を対象に書かれた書籍ですが、
『プログラミング.NET Framework』(マイクロソフトプレス、日経BP)
も参考になるかと思います。


ではでは(^^)/

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
ya
大ベテラン
会議室デビュー日: 2002/05/03
投稿数: 212
投稿日時: 2003-10-12 15:54
すみません、特定フィールドでの検索だったんですね。

なちゃさん等もおっしゃっているように Equals メソッドでしか ArrayList.IndexOf の動作は変更できません。
ただ、この場合、等価性を利用するのはちょっとまずい気がします。ほかのフィールドが違っていても等価であると判断してもよいか、となってこれは一般的には駄目でしょう。

特定フィールドでの検索機能を持ちたい場合は別にそのためのコレクションを持つ(すでに実装なさっている方法です)か、自分で作るのが一般的でしょう(ArrayList ではこの機能を実現するのは BinarySearch が一番近いですが、これは ArrayList が Sort されている必要があります)。

どうしてもいまの実装で美しくないと感じてしまうのなら、ArrayListを使わずそのデータの格納に特化した(TableField クラスしか格納できない & お望みの動作である Find メソッドみたいなものを実装している)コレクションクラスを自分で作りましょう。

ちなみに現状のコレクションを2つ持つという実装のままで行くなら、検索用のコレクションは、Hashtable か SortedList にしたほうがよいですよ。

スキルアップ/キャリアアップ(JOB@IT)