Java集合binarySearch无法正常工作

时间:2013-03-25 01:16:12

标签: java collections binary-search

我只是想尝试使用本机Java binarySearch,希望它总能找到第一次出现。但它并不总是返回第一次出现,我在这里做错了什么?

import java.util.*;

class BinarySearchWithComparator
{
  public static void main(String[] args)
  {
    // Please scroll down to see 'User' class implementation.
    List<User> l = new ArrayList<User>();
    l.add(new User(10, "A"));
    l.add(new User(10, "A"));
    l.add(new User(10, "A"));

    l.add(new User(20, "B"));
    l.add(new User(20, "B"));
    l.add(new User(20, "B"));
    l.add(new User(20, "B"));
    l.add(new User(20, "B"));
    l.add(new User(20, "B"));
    l.add(new User(20, "B"));


    l.add(new User(30, "C"));
    l.add(new User(30, "C"));
    l.add(new User(30, "C"));
    l.add(new User(30, "C"));

    Comparator<User> c = new Comparator<User>() {
      public int compare(User u1, User u2) {
        return u1.getId().compareTo(u2.getId());
      }
    };

    // Must pass in an object of type 'User' as the key.
    // The key is an 'User' with the 'id' which is been searched for.
    // The 'name' field is not used in the comparison for the binary search,
    // so it can be a dummy value -- here it is omitted with a null.
    //
    // Also note that the List must be sorted before running binarySearch,
    // in this case, the list is already sorted.
    Collections.sort(l,c);

    int index = Collections.binarySearch(l, new User(10, null), c);
    System.out.println(index);

    index = Collections.binarySearch(l, new User(20, null), c);
    System.out.println(index);

    index = Collections.binarySearch(l, new User(30, null), c);
    System.out.println(index);

    index = Collections.binarySearch(l, new User(42, null), c);
    System.out.println(index);
  }
}

class User {
  private int id;
  private String name;

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

  public Integer getId() {
    return Integer.valueOf(id);
  }
}

======

Result:
2 //not 0 ?
6 //not 3?
10 //ok
-15  ok

Why first 10 is not index 0 ?
Why first 20 is not index 3 ?
OK , 30 first occurrence is index 10

======================================

更新

现在看来它的API并不是gaurante那个!任何人都可以举例说明如何找到给定元素的第一次出现和最后一次出现(比如User(10,null)?

非常感谢。

4 个答案:

答案 0 :(得分:8)

Java并不保证它将返回的平等元素中的哪个元素。当你有相同范围的多个元素时,你需要向下走列表以找到与你要找的东西相等的第一个元素,如下所示:

User lookFor = new User(30, null);
index = Collections.binarySearch(l, lookFor, c);
while (index > 0 && c.compare(lookFor, l[index-1]) == 0) {
    index--;
}
// At this point the index is at the first element of the equal range

您可以将此逻辑包装在静态函数中,以避免每次都编写显式循环。

请注意,如果您的集合是随机访问,这将导致最坏情况的性能(当有许多相同的元素时)从O(lg(n))降级到O(n)。

答案 1 :(得分:5)

但是您的列表中有超过1个ID为10的元素。所以binarySearch 没有错误

binarySearch的Java手册说:

使用二进制搜索算法在指定列表中搜索指定的对象。在进行此调用之前,必须根据元素的自然顺序(通过sort(List)方法)将列表按升序排序。如果未排序,则结果未定义。 如果列表包含多个与指定对象相同的元素,则无法保证找到哪个元素。

http://docs.oracle.com/javase/6/docs/api/java/util/Collections.html#binarySearch(java.util.List,T)

答案 2 :(得分:1)

来自Array.binarySearch()上的javadoc:

Searches a range of the specified array of bytes for the specified value using the
binary search algorithm. The range must be sorted (as by the sort(byte[], int, int) 
method) prior to making this call. If it is not sorted, the results are undefined. 
If the range contains multiple elements with the specified value, there is no 
guarantee which one will be found.

答案 3 :(得分:1)

列表中的许多项目都是相同的,用于排序目的。如果两个项目具有相同的ID,那么从排序角度来看,使用哪个项目并不重要。 Collections.binarySearch只是将列表分成两半,直到找到匹配项。找到匹配后,它不会检查上一个项目是否也匹配,它只会返回索引。

考虑一个更强大的compareTo实现,如果项目具有ID,则不仅仅按ID进行排序。