C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[1]: Dictionary.containskeyを使ったキーの存


(過去ログ 64 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■36828 / inTopicNo.1)  Dictionary.containskeyを使ったキーの存
  
□投稿者/ えれこぜ (1回)-(2009/06/06(Sat) 15:18:57)

分類:[VB.NET/VB2005 以降] 

はじめまして。VB2005を使用しています。
ハッシュテーブルを使ったキーの存在チェックでハマっています。

Module Module1
    Sub Main()
        Dim 商品 As New 商品マスタ
        商品.JANコード = "4901234567890"
        商品.商品名 = "何かの商品"

        Dim dic As New Dictionary(Of 商品key, 商品マスタ)
        dic(商品.商品key) = 商品

        If dic.ContainsKey(商品.商品key) Then
            Console.WriteLine("あるよ!")
        Else
            Console.WriteLine("ないわ") ← False判定でこちらにきてしまう
        End If
    End Sub
End Module

Public Class 商品key
    Public JANコード As String
End Class

Public Class 商品マスタ
    Public JANコード As String
    Public 商品名 As String
    Public ReadOnly Property 商品key() As 商品key
        Get
            Dim key As New 商品key
            key.JANコード = Me.JANコード
            Return key
        End Get
    End Property
End Class

上記のようにハッシュテーブルにキーと値を入れ込み、
ContainsKeyにてキーの存在チェックをしているのですが、何故かFalse判定になってしまいます。
理由がさっぱり分かりません。どうかよろしくお願い致します。

引用返信 編集キー/
■36829 / inTopicNo.2)  Re[1]: Dictionary.containskeyを使ったキーの存
□投稿者/ Hongliang (412回)-(2009/06/06(Sat) 15:47:12)
Dictionary(Of TKey, TValue) のキーの同一性チェックは、コンストラクタで特に指定しなかった場合は EqualityComparer(Of TKey).Default が使用されます。
そして EqualityComaparer(Of T).Default は、T が IEquatable(Of T) を実装していない場合は Object.GetHashCode および Object.Equals が使用され、これらによって、インスタンスが同一かどうかが比較されます。
さて、商品.商品key プロパティは値を返すときに毎回新しい 商品key クラスのインスタンスを生成していますよね。当然、Dictionary に追加したときの商品key インスタンスとは別物のインスタンスですから、ContainsKey は False を返すことになります。

Dictionary のコンストラクタで IEqualityComparer(Of 商品key) を渡すか、商品key クラスに IEquatable(Of 商品key) を実装させるか、をすれば自由に同一性の判定をすることができます。
引用返信 編集キー/
■36830 / inTopicNo.3)  Re[1]: Dictionary.containskeyを使ったキーの存
□投稿者/ よねKEN (345回)-(2009/06/06(Sat) 15:48:30)
> 上記のようにハッシュテーブルにキーと値を入れ込み、
> ContainsKeyにてキーの存在チェックをしているのですが、何故かFalse判定になってしまいます。
> 理由がさっぱり分かりません。どうかよろしくお願い致します。

(1)クラス商品keyでGetHashCodeメソッドをオーバーライドしていない。
(2)商品マスタの商品keyプロパティは毎回異なる(商品keyクラスの)インスタンスを返している。

の2点が原因ですね。

提示のコード中の
> dic(商品.商品key) = 商品

ここの商品.商品keyと

> If dic.ContainsKey(商品.商品key) Then

ここの商品.商品keyとは(フィールドのJANコードの文字列が同じである)異なるインスタンスを使用しています。
つまり、別のキー を使っているのでDictionaryに見つからないという結果になります。

Dictionaryなどのハッシュテーブルでは、キーに利用するクラスのGetHashCodeメソッドの値をキーとして使います。
(1)によりクラス商品keyのGetHashCodeメソッドは、ObjectのGetHashCodeメソッド実装を使われます。
ObjectのGetHashCodeメソッドがどんな値を返すかは保証されていません。
(ただし現状提供されているObjectクラスの実装では、同じインスタンスなら、同じ値を返し、
異なるインスタンスでは異なる値を返すようにはなっています)

(2)の理由により、商品.商品keyが毎回異なるインスタンスを返すので、提示のコードではContainsKeyはFalseにしかなりません。

引用返信 編集キー/
■36831 / inTopicNo.4)  Re[1]: Dictionary.containskeyを使ったキーの存
□投稿者/ よねKEN (346回)-(2009/06/06(Sat) 15:54:53)
Hongliangさんの投稿内容とかぶりましたね。
私の No36830 の投稿の以下の部分は、

> Dictionaryなどのハッシュテーブルでは、キーに利用するクラスのGetHashCodeメソッドの値をキーとして使います。

少し正確ではなかったので、詳細はHongliangさんの投稿を参照してください。

それはそうとして、商品keyを用意しなくても以下のようにStringをキーにすればよいかと思います。

>         Dim dic As New Dictionary(Of 商品key, 商品マスタ)
>         dic(商品.商品key) = 商品
> 
>         If dic.ContainsKey(商品.商品key) Then

Dim dic As New Dictionary(Of String, 商品マスタ)
dic(商品.商品Key) = 商品

If dic.ContainsKey(商品.商品key) Then


> Public Class 商品key
>     Public JANコード As String
> End Class

このクラスは削除します。
 
>     Public ReadOnly Property 商品key() As 商品key
>         Get
>             Dim key As New 商品key
>             key.JANコード = Me.JANコード
>             Return key
>         End Get
>     End Property

     Public ReadOnly Property 商品key() As String
         Get
             Return Me.JANコード
         End Get
     End Property

引用返信 編集キー/
■36832 / inTopicNo.5)  Re[2]: Dictionary.containskeyを使ったキーの存
□投稿者/ えれこぜ (2回)-(2009/06/06(Sat) 17:34:15)
Hongliangさん、よねKENさん、返答ありがとうございました。
インスタンスが違う事には気づきませんでした。
今回HashCodeというものに初めて気づかされたのですが、
「GetHashCodeで返される値が同じであれば、同一のインスタンスである。」と判断するという事なんでしょうか。
Stringクラスで同じ文字列であれば、同じ値を返すようなので、同一のインスタンスとも違うのでしょうね。

> Dictionary のコンストラクタで IEqualityComparer(Of 商品key) を渡すか、商品key クラスに IEquatable(Of 商品key) を実装させるか、をすれば自由に同一性の判定をすることができます。

Dim dic As New Dictionary(Of 商品key, 商品マスタ)(New 商品KeyComparer) ← 追加しました

Public Class 商品KeyComparer
    Implements IEqualityComparer(Of 商品key)
    Public Function Equals1(ByVal x As 商品key, ByVal y As 商品key) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of 商品key).Equals
        If x.JANコード = y.JANコード Then
            Return True
        End If
    End Function
    Public Function GetHashCode1(ByVal obj As 商品key) As Integer Implements System.Collections.Generic.IEqualityComparer(Of 商品key).GetHashCode
        'ここにはいったい何が・・・
    End Function
End Class

上記のような、Dictionary のコンストラクタで IEqualityComparer(Of 商品key) を渡す方法で、得たい結果が出ました。
商品key クラスに IEquatable(Of 商品key) を実装させるほうが自分的にすっきりするので、こちらは今から勉強します。
一番最初に指摘して頂いたGetHashCodeを実装する方法はやはりさっぱりです^^;

> それはそうとして、商品keyを用意しなくても以下のようにStringをキーにすればよいかと思います。

よくばって複数キーで索引する仕組みを作ろうと思っていました。
自分のやりたい事だと複数キーでも文字連結で出来そうな気がします。

ありがとうございました。

解決済み
引用返信 編集キー/
■36833 / inTopicNo.6)  Re[3]: Dictionary.containskeyを使ったキーの存
□投稿者/ えれこぜ (3回)-(2009/06/06(Sat) 18:50:09)
	
Public Class 商品key
    Implements IEquatable(Of 商品key)

    Public JANコード As String

    Public Function Equals1(ByVal other As 商品key) As Boolean Implements System.IEquatable(Of 商品key).Equals
        If Me.JANコード.Equals(other.JANコード) Then
            Return True
        End If
    End Function

    Public Overrides Function GetHashCode() As Integer
        Return Me.JANコード.GetHashCode
    End Function
End Class

宿題にしていた商品keyクラスにIEquatableを実装するほうも、これで出来た(?)と思います。
ありがとうございました。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -