質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.46%

  • Java

    16225questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

クラスリストの比較でcontainsが動作してくれない

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 10K+

v_root

score 43

containsを用いて2つのクラスリストを比較したいのですが、うまく動作してくれません。

class Order{
    public int id;
    public String name;
    public Order(int id,String name){
        this.id = id;
        this.name = name;        
    }
}
class Food{
    public int id;
    public String name;
    public Food(int id,String name){
        this.id = id;
        this.name = name;
    }
}

public class App {
    public static void main(String[] args) {

        List<Order> orderList = new ArrayList<>();
        List<Food> foodList = new ArrayList<>();

        orderList.add(new Order(1,"アップル"));
        orderList.add(new Order(2, "バナナ"));
        orderList.add(new Order(3, "綿菓子"));

        foodList.add(new Food(1,"アップル"));
        foodList.add(new Food(2, "バナナ"));

        for (int i = 0; i < orderList.size(); i++) {
//比較してくれない
            if(!foodList.contains(orderList.get(i).name)){
                System.out.println(orderList.get(i).name);
            }

        }
    }


foodListとorderListを比較し、foodListにないものを参照しようとしています。なぜかうまく比較してくれないので、疑問でした。なぜうまく比較してくれないのか、また解決策を教えてください。

ちなみに整数型リストは比較してくれました。

public class App {
    public static void main(String[] args) {
        List<Integer> lst1 = new ArrayList<>();
        List<Integer> lst2 = new ArrayList<>();

        lst1.add(0);
        lst1.add(1);

        lst2.add(0);
        lst2.add(1);
        lst2.add(2);

        for (int index = 0;index < lst2.size();index++) {
//ちゃんと比較してくれた
            if(!lst1.contains(lst2.get(index))){
                System.out.println(lst2.get(index));
            }
        }
    }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

containsの判定にはequalsを使います。デフォルトでは「同じオブジェクト」の時だけtrueとなるので、現状等しいオブジェクトを見つけられません。
equalsをオーバーライドして、等しいの定義付けをする必要があります。

ここからは構成上の話になりますが、FoodクラスとOrderクラスというのは不自然です。「食べ物」を意味するクラスFoodがあり、そのFoodが「注文されたもの」か「在庫にあるもの」かという色づけがされるわけです。

率直に考えればこうなります。

class Food{
    public int id;
    public String name;
    public Food(int id,String name){
        this.id = id;
        this.name = name;
    }

    public boolean equals(Object o) {
        if (o == this) return true;
        if (o.getClass() != this.getClass()) return false;
        Food f = (Food)o;
        return this.id == f.id && this.name.equals(f.name);
    }
    //hashcodeの実装は省略
}

public class App {
    public static void main(String[] args) {

        List<Food> orderList = new ArrayList<>();
        List<Food> foodList = new ArrayList<>();

        orderList.add(new Food(1,"アップル"));
        orderList.add(new Food(2, "バナナ"));
        orderList.add(new Food(3, "綿菓子"));

        foodList.add(new Food(1,"アップル"));
        foodList.add(new Food(2, "バナナ"));

        for (int i = 0; i < orderList.size(); i++) {
            if(!foodList.contains(orderList.get(i))){
                System.out.println(orderList.get(i));
            }

        }
    }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

swordoneさんのおっしゃっている通りですが、

foodList.contains(orderList.get(i).name)

では、例えば、Food(1,"アップル")とString("アップル")とを比較することになり、containsメソッドが呼び出す
"アップル".equals(Food(1,"アップル")) // 注意:説明のためJavaの文法に不正確 
はfalseを返します。

よって、正しく比較させるためには、Foodクラスのインスタンスと正しく比較できるequalsメソッドをOrderクラスでオーバーライドする必要があります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/23 13:31

    ありがとうございます。その方法で試したら成功しました!

    キャンセル

  • 2016/11/23 17:47

    いや、文法上なにも間違っていませんよ。

    キャンセル

  • 2016/11/23 17:52

    ?

    キャンセル

  • 2016/11/23 19:18

    あ、失礼。この回答の「説明のためJavaの文法に不正確」という部分に対するコメントでした。

    キャンセル

checkベストアンサー

-1

equalをオーバーライドした解決策

import java.util.*;

class Order{

public boolean equals(Object o){

if((o instanceof Food) && (((Food)o).id==this.id) && (((Food)o).name.equals(this.name))){
return true;

}else return false;
}

    public int id;
    public String name;
    public Order(int id,String name){
        this.id = id;
        this.name = name;        
    }
}
class Food{




    public int id;
    public String name;
    public Food(int id,String name){
        this.id = id;
        this.name = name;
    }
}

public class App {
    public static void main(String[] args) {

        List<Order> orderList = new ArrayList<>();
        List<Food> foodList = new ArrayList<>();

        orderList.add(new Order(1,"アップル"));
        orderList.add(new Order(2, "バナナ"));
        orderList.add(new Order(3, "綿菓子"));

        foodList.add(new Food(1,"アップル"));
        foodList.add(new Food(2, "バナナ"));
        for (int i2 = 0; i2 < orderList.size(); i2++) {

//比較してくれない
            if(!(foodList.contains(orderList.get(i2)))){
                System.out.println(orderList.get(i2).name);
            }

        }
    }
}

反射性等が問題になるという旨指摘がありましたので現在訂正した部分までを下記に記載しておきます
再訂正

import java.util.*;

class Order{
public boolean equals(Object o){
if((o instanceof Food) && (((Food)o).id==this.id) && (((Food)o).name.equals(this.name))){
return true;

}
else if((o instanceof Order) && (o.hashCode()==this.hashCode())){
return true;

}
return false;
}
public int hashCode(){


char[] cA = name.toCharArray();

int ch=0;

for(char t:cA){
int g=(int)t;


ch+=g;

}


return id*1000+ch;

}

public int id;
public String name;
public Order(int id,String name){
this.id = id;
this.name = name; 
}
}
class Food{


public int hashCode(){


char[] cA = name.toCharArray();

int ch=0;

for(char t:cA){

int g=(int)t;
ch+=g;

}


return id*1000+ch;

}



public int id;
public String name;
public Food(int id,String name){
this.id = id;
this.name = name;
}
}

public class App {
public static void main(String[] args) {

List<Order> orderList = new ArrayList<>();
List<Food> foodList = new ArrayList<>();

orderList.add(new Order(1,"アップル"));
orderList.add(new Order(2, "バナナ"));
orderList.add(new Order(3, "綿菓子"));

foodList.add(new Food(1,"アップル"));
foodList.add(new Food(2, "バナナ"));
for (int i2 = 0; i2 < orderList.size(); i2++) {

//比較してくれない
if(!(foodList.contains(orderList.get(i2)))){
System.out.println(orderList.get(i2).name);
}



}
Order o = new Order(1, "アップル");
Food f = new Food(1, "アップル");
System.out.println(o.hashCode() +" :" + f.hashCode());
System.out.println("o.equals(o) = " + o.equals(o));

System.out.println("f.equals(o) = " + f.equals(o));
System.out.println("o.equals(f) = " + o.equals(f));

System.out.println("o.hashCode() = " + o.hashCode());
System.out.println("f.hashCode() = " + f.hashCode());
Order o2 = new Order(1, "アップル");
Order p2 = new Order(1, "パップル");

 Food f2 = new Food(1, "アップル");
System.out.println(p2.hashCode()+" :" +o2.hashCode()+" :" +f2.hashCode());
System.out.println("p2.equals(o2) = " + p2.equals(o2));
 System.out.println("o2.equals(f2) = " + o2.equals(f2));
 System.out.println("p2.equals(f2) = " + p2.equals(f2));


}
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/23 13:29

    ありがとうございます。勉強になりました。

    キャンセル

  • 2016/11/23 19:55

    この実装は、以下の理由により不適切です。

    ・equals メソッドの一般契約のうち、「反射性」および「対称性」に違反している

     http://docs.oracle.com/javase/jp/7/api/java/lang/Object.html#equals(java.lang.Object)
     > 反射性 (reflexive): null 以外の参照値 x について、x.equals(x) は true を返します。
     > 対称性 (symmetric): null 以外の参照値 x および y について、y.equals(x) が true を返す場合に限り、x.equals(y) は true を返します。

    ・hashCode メソッドを適切にオーバーライドしていない

     http://docs.oracle.com/javase/jp/7/api/java/lang/Object.html#hashCode()
     > equals(Object) メソッドに従って 2 つのオブジェクトが等しい場合は、2 つの各オブジェクトに対する hashCode メソッドの呼び出しによって同じ整数の結果が生成される必要があります。

    例 :
     // Orderクラスおよび Foodクラスの実装は省略
     public class App {
       public static void main(String[] args) {
         Order o = new Order(1, "アップル");
         Food f = new Food(1, "アップル");

         System.out.println("o.equals(o) = " + o.equals(o));

         System.out.println("f.equals(o) = " + f.equals(o));
         System.out.println("o.equals(f) = " + o.equals(f));

         System.out.println("o.hashCode() = " + o.hashCode());
         System.out.println("f.hashCode() = " + f.hashCode());
       }
     }

    実行結果 :
     o.equals(o) = false
     f.equals(o) = false
     o.equals(f) = true
     o.hashCode() = 1335080452
     f.hashCode() = 1788012908


    このコードは回答で示したケースで【のみ】うまく動作しますが、
    それ以外の equalsメソッドや hashCode メソッドに基づいて動作するコード(※)では、
    うまく動かない可能性があります。

    ※ 例えば、Listインターフェースの indexOfメソッドや HashSetクラスを使用する場合など。

    キャンセル

  • 2016/11/23 23:34

    以下のコードではnameが1000文字以下の場合は
    equalsがfalseでハッシュコードが同じ場合はあっても
    equalsがtrueの場合ハッシュコードは同じになり
    オーバーライドで従うべき規則に従っていることになります
    nameが1000文字以上になると規則から外れうるので完全ではないでしょうが
    今回使われると思しき文字数の範囲だと規則に従えると思います

    import java.util.*;

    class Order{

    public int hashCode(){

    return id*1000+name.length();

    }

    public boolean equals(Object o){
    if((o instanceof Food) && (((Food)o).id==this.id) && (((Food)o).name.equals(this.name))){
    return true;

    }
    else if((o instanceof Order) && (o.hashCode()==this.hashCode())){
    return true;

    }
    return false;
    }

    public int id;
    public String name;
    public Order(int id,String name){
    this.id = id;
    this.name = name;
    }
    }
    class Food{


    public int hashCode(){

    return id*1000+name.length();

    }


    public int id;
    public String name;
    public Food(int id,String name){
    this.id = id;
    this.name = name;
    }
    }

    public class App {
    public static void main(String[] args) {

    List<Order> orderList = new ArrayList<>();
    List<Food> foodList = new ArrayList<>();

    orderList.add(new Order(1,"アップル"));
    orderList.add(new Order(2, "バナナ"));
    orderList.add(new Order(3, "綿菓子"));

    foodList.add(new Food(1,"アップル"));
    foodList.add(new Food(2, "バナナ"));
    for (int i2 = 0; i2 < orderList.size(); i2++) {

    //比較してくれない
    if(!(foodList.contains(orderList.get(i2)))){
    System.out.println(orderList.get(i2).name);
    }
    /*


    }
    Order o = new Order(1, "アップル");
    Food f = new Food(1, "アップル");

    System.out.println("o.equals(o) = " + o.equals(o));

    System.out.println("f.equals(o) = " + f.equals(o));
    System.out.println("o.equals(f) = " + o.equals(f));

    System.out.println("o.hashCode() = " + o.hashCode());
    System.out.println("f.hashCode() = " + f.hashCode());*/
    }
    }

    キャンセル

  • 2016/11/24 10:30 編集

    今度は推移性に違反しています。

    http://docs.oracle.com/javase/jp/7/api/java/lang/Object.html#equals(java.lang.Object)
    > 推移性 (transitive): null 以外の参照値 x、y、および z について、x.equals(y) が true を返し、y.equals(z) が true を返す場合、x.equals(z) は true を返します。

     public class App {
      public static void main(String[] args) {

      Order o = new Order(1, "アップル");
      Order p = new Order(1, "パップル");

      Food f = new Food(1, "アップル");

      System.out.println("p.equals(o) = " + p.equals(o));
      System.out.println("o.equals(f) = " + o.equals(f));
      System.out.println("p.equals(f) = " + p.equals(f));
      }
     }

    実行結果 :
     p.equals(o) = true
     o.equals(f) = true
     p.equals(f) = false

    キャンセル

  • 2016/11/24 10:37 編集

    別に外部に公開するクラスというわけでもないので、そこまで厳密性を求める必要はないのでは…
    まあそもそも、継承関係も共通するインタフェースもない別のクラスのオブジェクトが等しいと判定すること自体無理があるのですが。

    キャンセル

  • 2016/11/24 13:14

    swordone様

    > 別に外部に公開するクラスというわけでもないので、そこまで厳密性を求める必要はないのでは…

    自分のプログラムに組み込むだけなら、私も仰る通りだと思います。

    ただ、QAサイトの回答に載っているコードは
    広義の意味で「外部に公開」していると言えるのではないでしょうか?

    この回答を参考にコードを書こうとする方もいると思いますので。


    > まあそもそも、継承関係も共通するインタフェースもない別のクラスのオブジェクトが等しいと判定すること自体無理があるのですが。

    ですよねw

    キャンセル

  • 2016/11/24 19:05 編集

    回答へ

    キャンセル

  • 2016/11/24 21:42

    回答編集で訂正願います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.46%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Java

    16225questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。