• 締切済み

Collections.binarySearchについて

Collections.binarySearchを使うには、ソートする必要があることがわかりますが、ソートしない場合、結果はどうなるのはちょっと気になってしまっているので質問させてください。 例:public class char6_33 { public static void main(String args[]){ List list=new ArrayList(); list.add("b"); list.add("a"); list.add("c"); System.out.println(Collections.binarySearch(list,"a")); System.out.println(Collections.binarySearch(list,"b")); System.out.println(Collections.binarySearch(list,"c")); } 実行してみたら、結果は 1 -3 2 になるですが、なぜでしょうか。 もしSystem.out.println(Collections.binarySearch(list,"a"));の前にCollections.sort(list);を追加すれば、結果は0 1 2になるのは理解するできが、ソートしない場合はなぜ1 -3 2 になるのはちょっと理解できません。ご存知の方はぜひご教授ください。よろしくお願いします。

  • Java
  • 回答数4
  • ありがとう数4

みんなの回答

  • foxa-gogo
  • ベストアンサー率44% (38/85)
回答No.4

>まず、 >indexedBinarySearchというのはどういうのタイミングで呼ばれますかね?? >もしlistはちゃんとソートされた場合もindexedBinarySearchが呼ばれるでしょうか。 #3の方が回答されたとおり、binarySearch()の中で渡されたリストがランダムアクセス可能かどうか調べ、可能であればこの関数、可能でなければ別の内部関数を呼んでいます。 >あと、 >>int cmp = compare(midVal, key); //compare("a","a") = 0(もしキ>ーがbならcompare("a","b")は負数を返し、low = mid+1 >これもよくわかりません、compareどういう動きなのかよくわかりません。 ここは実はソースをはしょって書いたのですが、本当は Comparable midVal = list.get(mid); int cmp = midVal.compareTo(key); となっています。 Stringオブジェクトは、インターフェースComparableを実装しており、これは「比較可能」という意味です。 インターフェースComparableのメソッドがcompareToで、Stringの場合、辞書順で自分のほうが若ければ負数を、後ろのほうであれば正の数を返します。 すなわち、"abc".compareTo("xyz")は負数を、逆は正の数を返します。 (とりあえず#3の方が回答されたとおりなのですが) list = { "One", "Three", "Two" }なので、アルファベット順にソートはされています。 さて、今回はStringのcompareToメソッドではなく、sortとして渡したComparatorを使うよう指定しています。 binarySearchではまず中央の値と、キーを比較します。なので、compare("Three", "Two")というふうにわたされますが、内部で"Two".compareTo("Three")となり、自分のほうが後ろなので、正の数を返します。 binarySearchは、正の数が帰ってきたので、探しているキーは、「"Three"よりも若いのだな」、と判断します。真ん中がlist[1]だったので、次はlist[0]と比較しますが、compareが"Two".compareTo("One")で、また正の数が返り、binarySearchは「キーはlist[0]よりもさらに若いのだな」と判断します。でももう0以上若い要素はないので、そこで検索終了、見つからなかったことになります。 Comparatorが期待するようにソートされていればよい(結果的にソートされていれば、明示的にソートする必要はない)ので、もしlist = { "Two", "Three", "One"}であったなら、見つかって0が返ったものと考えられます。(本当はmidValにキーよりも若いのか後ろなのか聞かなければいけないのに、キーにmidValよりも若いのかうしろなのか聞いているので、ちょうど反対となり、辞書順の反対に並んでいればComparatorが期待する順でソートされていることになります) ちなみに僕もJavaプログラマの試験勉強中です。。。重箱の隅をつつくような問題ばっかりで、嫌になっちゃいますねぇ。。。。。まぁ頑張りましょう!

  • PecoPlus
  • ベストアンサー率76% (144/188)
回答No.3

 こんにちは。  ↓ここがおかしいです。 class SortMethod implements Comparator<Employee>{   public int compare(Employee emp1,Employee emp2){     return emp2.getId().compareTo(emp1.getId());          ↑ここ   } }  Comparator の compare メソッドは、「第一の引数が第二の引数に比べてどうか?」というものなので、指摘の部分は逆。  つまり。 return emp1.getId().compareTo(emp2.getId());  と、ならなければなりません。  この問題では、リストは順番通りになっていますが、比較が狂っているので、間違った答えが出るのも無理はありません。  ちなみに、indexedBinarySearchメソッドは、binarySearchメソッドに与えられたリストが、ランダムアクセス可能な場合に呼び出されます。  #2さんのリンク先のソースコードで、binarySearchメソッドから、たどって行ってみてください。

  • foxa-gogo
  • ベストアンサー率44% (38/85)
回答No.2

http://www.docjar.com/html/api/java/util/Collections.java.html を、private static <T> int indexedBinarySearchで検索すると、どういう動きになっているか、ソースが見れます。 (以下汚くてスミマセン) int low = 0; int high = list.size - 1 //=2 while (low <= high){      int mid = (low + high) >>> 1; //010 >>> 1 = 001 = 1 (シフト演算)       midVal = list.get(mid); //list.get(1) = "a"       int cmp = compare(midVal, key); //compare("a","a") = 0(もしキーがbならcompare("a","b")は負数を返し、low = mid+1          if (cmp < 0)            low = mid + 1;           else if (cmp > 0)            high = mid - 1;           else            return mid; // cmp = 0         }           return -(low + 1); // key not found       } キーが"b"の場合、初回でlow = mid + 1;が実行され、low = 2, (low + high) >>> 1 は、(0100) >>> 1 = 0010 = 2でmid = 2, list.get(2) = "c"で、compare("c", "b")は正の数を返し、よってhigh = 2-1, よってwhile (low <= high)がwhile(2<=2-1) = while(2<=1)となりfalse,したがってreturn -(2+1) = return (-3)となります。 この返り値の3という値も何かうまく使う方法があるのでしょうが、とりあえず負数を返すことでnot foundを表しています。 まんなかのlist.get(1)を見たら、"a"だったので、"b"はこれよりもあとだなと思って、list.get(2)をみたら"c"だった。じゃぁもう"b"はあるわけないよね、ということで-3が返っています。

j048047f
質問者

お礼

foxa-gogoさん ご回答ありがとうございました。 勉強不足ですみませんが、書いていただいてことはよく理解できません。以下のように質問させていただきます。 まず、 indexedBinarySearchというのはどういうのタイミングで呼ばれますかね?? もしlistはちゃんとソートされた場合もindexedBinarySearchが呼ばれるでしょうか。 あと、 >int cmp = compare(midVal, key); //compare("a","a") = 0(もしキ>ーがbならcompare("a","b")は負数を返し、low = mid+1 これもよくわかりません、compareどういう動きなのかよくわかりません。 さらに、恐縮ですが、追加質問させていただきます。実は今「徹底攻略 Java2 プログラマ問題集」という問題集を勉強しておりますが、この中のもうひとつのCollections.binarySearch問題に困っています。 以下の通りです。 以下のプログラムをコンパイルし、実行した結果として何でしょう。 List<Employee> list=new ArrayList<Employee>(); list.add(new Employee("One")); list.add(new Employee("Three")); list.add(new Employee("Two")); SortMethod sort=new SortMethod(); int i=Collections.binarySearch(list,new Employee("Two"),sort); System.out.println(i); import java util.*; class Employee{ private String id; Employee(String id){ this.id=id; } public String getId(){return id;} public String toString(){return this.id+"";} class SortMethod implements Comparator<Employee>{ public int compare(Employee emp1,Employee emp2){ return emp2.getId().compareTo(emp1.getId()); } } この問題の答えは-1です。ここでもソートしてなくて、Collections.binarySearchを利用したんですよね。でもなぜ-1になるのかどうしても分からなくて、すみませんが、まとめてご教授していただければ幸いと存知ます・

  • Yanch
  • ベストアンサー率50% (114/225)
回答No.1

API リファレンスを読みましょう。 http://java.sun.com/javase/ja/6/docs/ja/api/java/util/Collections.html#binarySearch(java.util.List,%20T) > リストがソートされていない場合、結果は定義されません。 とあるように、ソートされていない List に対して、binarySearchを使用してはいけません。

j048047f
質問者

お礼

なるほど、ということはリストがソートされていない場合は、どういう結果が出るのか決まってないんですね。 早速回答ありがとうございました。

関連するQ&A

  • StringクラスのcompareToメソッド

    ArrayListに登録した文字列を五十音順にソートしようと思いComparator を使用して 以下のようなサンプルプログラムを作ってみました。 ところが想定していたような {赤ちゃん、富士山、山口県}とはならず {富士山、山口県、赤ちゃん} というような結果になりました。 compare() の戻り値の部分を return ((String)arg1).compareTo((String)arg0); に変更しても{赤ちゃん、山口県、富士山} となり辞書の並びとは異なる結果になりました。 辞書順に並べるにはなにかよい方法はありますでしょうか。 public class compareTest { public static void main(String[] args) { ArrayList<String> array = new ArrayList<String>(); String a = "赤ちゃん"; String b = "山口県"; String c = "富士山"; array.add(a); array.add(b); array.add(c); for(int i=0;i<array.size();i++) { System.out.println("ソート前=" + array.get(i)); } Collections.sort(array, new testComp()); for(int i=0;i<array.size();i++) { System.out.println("ソート後=" + array.get(i)); } } } public class testComp implements Comparator { public int compare(Object arg0, Object arg1) { return ((String)arg0).compareTo((String)arg1); } }

    • ベストアンサー
    • Java
  • ArrayListのcloneメソッド

    お世話になります。 ArrayListのcloneメソッドなんですが、API上は「ArrayList のインスタンスのシャローコピーを返します。要素自体はコピーされません。 」と記載がありますが、 「ディープコピー」をしているような感じがして、なぜ「シャローコピー」と言っているのか、教えて欲しいです。 しかも、要素自体もコピーされているような…。 自分の理解では、 シャローコピー:コピー元、コピー先で同じオブジェクトを参照する ディープコピー:コピー元、コピー先で違うオブジェクトを参照する 試したソースは以下です。 --------------- import java.util.ArrayList; public class Test { public static void main(String[] args) { ArrayList<String> array = new ArrayList<String>(); array.add("a"); array.add("b"); ArrayList<String> array2 = (ArrayList<String>) array.clone(); array2.add("c"); System.out.println("array:" + array); System.out.println("array2:" + array2); } } --------------- cloneメソッドはシャローコピーなので、array2で「c」がaddされたら、arrayも「c」が追加されて、 array:[a, b, c] array2:[a, b, c] となるはずが、 array:[a, b] array2:[a, b, c] となります。 array、array2は別々のオブジェクトを参照しているような気がします。 恐らく大きな勘違いをしているのかもしれませんが、 調べてもいまいち理解できませんでした。 お手数おかけしますが、よろしくお願い致します。

    • ベストアンサー
    • Java
  • Java初心者です。どうぞお教えください。

    CSVファイルを読み込んで、ソートしてから、現在日時のファイル名でCSVで書き出します。以下のファイルでは、CSVで書き出せません。現在日時のファイル名をつける方法もよくわかりません。どうぞ教えてください。importは省略しています。 public class CSVdata { public static void main(String[] args) { String fname = " " ; ArrayList<String> list = new ArrayList<String>(); String save_filename = getFileName(); if(args.length>0) { fname = args[0]; } try { BufferedReader reader = new BufferedReader( new FileReader(fname) ); String Line = null; while ((Line = reader.readLine()) != null) { list.add(Line); } reader.close(); Collections.sort(list ); PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(save_filename + ".csv"))); for (String string : list) { writer.println(string); } writer.close(); } catch (FileNotFoundException e) { System.out.println("ファイルがありません"); } catch (IOException e) { System.out.println("ファイルがありません"); } } public static String getFileName(){ Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); return sdf.format(d); } }

  • 同期化時のことで

    いつもお世話になっています。 同期化を使用した場合のことで質問します。 private static final List<String> list = Collections.synchronizedList(new ArrayList<String>() ); を使いサーブレットでの同期化を実行しているのですが、 この場合に、値をソート化して処理を実行することは可能なのでしょうか? Collectionsを調べると、可能性としてsynchronizedSetだとできそうな気はするのですが、 その際の直列化の実装がよくわかりません。 考えているソースは以下です。 ** ラップオブジェクト同期化 **/ SortedSet sort = Collections.synchronizedSortedSet(new TreeSet<String>() ); /** サーブレットクラスでの処理 **/ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //リクエストデータ String key = request.getParameter("key"); //リクエストデータをSortedSetに格納する sort.add(key); //←この時点で、直列化のための何らかの処理がいる try{ //実行時、時間をずらすために宣言 Thread.sleep(1000); }catch(Exception e){ } //何の処理もしなかったら、実行された画面の順で処理が開始される synchronized(sort){ for(int a=0;a<10;a++){ System.out.println("a="+a); } } //SortedSetに格納された値の順 List sortList = new ArrayList(sort); for(int s=0;s<sortList.size();s++ ){ //↓すべての処理が終わってみたら、確かにソート化されているs System.out.println("sortList["+s+"]="+sortList.get(s)); } } jsp側のリクエストデータ 1回目 C 2回目 B 3回目 A 実行させたい順 C→A→B このとき、B,Aが渡されるタイミングは、Cの処理が実行中時 また、どのキーに対しての実行中なのかを見ることはできるのでしょうか? Collectionsクラスを見たところなさそうだったのですが。。。 宜しくお願いします。

    • ベストアンサー
    • Java
  • binarySearchについて

    こんにちは、Javaを勉強しているものです。 binarySearchをしたとき、存在しない値を検索すると負の値が戻されると聞きましたが、その数値はどのようにして決められるのでしょうか? import java.util.*; class Sample { public static void main(String[] args){ String[] a ={"he", "is", "a", "boy"}; List<String> li = Arrays.asList(a); for(String s : a) System.out.print(S + ":"); System.out.println(); Collections.sort(li); for(String s: li) System.out.print(s + ":"); System.out.println(); System.out.println(Collections.binarySearch(li, "boy")); System.out.println(Collections.binarySearch(li, "girl")); Collections.reverse(li); for(String s: li) System.out.print(S + ":"); } } このコードの実行結果は、 he:is:a:boy: a:boy:he:is: 1 -3 is:he:boy:a: となります。 "girl"は、リストに存在しないので負の値である"-3"が返されたわけですが、3という数字がどのような理由で出てきたのか教えてください。 宜しくお願い致します。

  • コレクションクラスについて

    ●下記のコードについて質問があります import java.util.*; public class Test { public static void main(String args[]) { ArrayList<ObjectOne> list = new ArrayList<ObjectOne>(); list.add(new ObjectOne()); list.add(new ObjectOne()); list.add(new ObjectOne()); Collections.sort(list); } } class ObjectOne { private int x = 0; private int y = 0; } このソースをコンパイルすると、 シンボル: メソッド sort(java.util.ArrayList<ObjectOne>) 場所 : java.util.Collections の クラス Collections.sort(list); と、エラーが表示されてしまいます。 java.util.*をインポートしているので、上記のようなエラーはでないと 思うのですが、うまくいかないです。おそらく、ObjectOneクラスで 何か処理漏れが起きているのかもしれませんが、エラーとなる原因を 特定することができません。 エラーとなる原因と解消する手立てを教えていただければと思っております。 宜しくお願い致します。 「追記」 ArrayList<ObjectOne> list = new ArrayList<ObjectOne>(); の<ObjectOne>を消せばエラーはなくなりますが、 <ObjectOne>を消さない方針で考えがあればと思っております。

  • Javaのプログラムについて教えてください!

    Genericsを使ってエラーの出ないようにするにはどうすればいいですか? import java.util.*; public class Sample{ public static void main(String[] args){ ArrayList ary = new ArrayList(); ary.add("Mac"); ary.add("Wiindows"); ary.add("Linux"); for(Object str:ary){ System.out.println((String)str); } } }

    • ベストアンサー
    • Java
  • 静止的フィールドについて教えてください

    教えてください。以下のプログラムだとして、 class Box { static int a = 0 ; int b = 0 ; int c = a+4; } class sample { public static void main(String[] args) { Box n = new Box() ; n.a-- ; n.b-- ; n.c[1]-- ; System.out.println("n.a= "+n.a);//-1 System.out.println("n.b ="+n.b);//-1 System.out.println("n.c[2]="+n.c[2]);//4 Box m = new Box() ; m.a++ ; m.b++ ; m.c[2]++ ; System.out.println("n.a ="+n.a);//0 System.out.println("n.b ="+n.b);//-1 System.out.println("n.c[2] ="+n.c[2]);//4 System.out.println("m.a ="+m.a);//0 System.out.println("m.b ="+m.b);//1 System.out.println("m.c[2] ="+m.c[2]);//4 } } Box nの中のn.aの値はわかります。 ですが、Box mの中のn.aは、a がstaticフィールド(?)なので元の0に戻りますが、m.aがなぜ0なのかわかりません。 そもそもstatic int = ●; のときは、静止的intと教わったのですが、どういう現象が起こるのかいまいちです。 よろしくお願いします。

    • ベストアンサー
    • Java
  • java iを1づつ増やすプログラムと2づつ増やすプログラム

    次のようにすればiを1づつ増やして表示されます。 class Calc{   int i=1;   int add(){     return i++;   } } class Count{   public static void main(String[] args){     Calc calc = new Calc();     System.out.println("i = " + calc.add());     System.out.println("i = " + calc.add());     System.out.println("i = " + calc.add());   } } 実行結果 i = 1 i = 2 i = 3 しかし次のように2づつ増やそうとすると、 class Calc{   int i=1;   int add(){     return i+2;   } } class Count{   public static void main(String[] args){     Calc calc = new Calc();     System.out.println("i = " + calc.add());     System.out.println("i = " + calc.add());     System.out.println("i = " + calc.add());   } } 実行結果 i = 3 i = 3 i = 3 このようになってしまいます。どこがおかしいのでしょうか?

    • ベストアンサー
    • Java
  • ArrayList の変数をaddしてもアドレスが変化しない

    windowsXP Eclipse3.4で import java.util.ArrayList; ArrayList list = new ArrayList(); Bean bean = new Bean(); list.add(bean); list.add(bean); System.out.println(bean); System.out.println(list); とアドレスを出力してみると beanのアドレスとlist内の二つのアドレスと3つのアドレスが すべて同じになってしまいます。 なにが原因か分からないのですが、分かる方がいましたら教えてください。 よろしくお願いします。

    • ベストアンサー
    • Java

専門家に質問してみよう