关于自定义对象排序问题的集合二进制搜索

时间:2018-07-08 02:44:20

标签: java collections

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Mytest {
    public static void main(String... args) {
        searchperson();
    }

    private static void searchperson() {
        List<Person> personList = new ArrayList<>();
        for (int i = 1; i < 6; i++) {
            Person p = new Person(String.valueOf(i), String.valueOf(i), i);
            personList.add(p);
        }
        Comparator<Person> sortComp = new Comparator<Person>() {
            public int compare(Person p1, Person p2) {
                return p1.getAge() < p2.getAge() ? 1 : -1;
            }
        };
        Comparator<Person> searchComp = new Comparator<Person>() {
            public int compare(Person p1, Person p2) {
                return String.valueOf(p1.getAge()).compareTo(String.valueOf(p2.getAge()));
            }
        };
        Collections.sort(personList, sortComp);
        Person p = new Person("2", "2", 2);
        int indx = Collections.binarySearch(personList, p, searchComp);
        System.out.println("index is:" + indx);
    }
}

class Person {
    String firstName;
    String lastName;
    int age;

    public Person(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

    public int getAge() {
        return this.age;
    }
}

此打印索引为:1,这是预期值。 但是,当我稍微更改排序逻辑(即)而不是p1.getAge() > p2.getAge()时,我只是将其更改为p1.getAge() < p2.getAge()

Comparator<Person> sortComp = new Comparator<Person>() {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getAge() < p2.getAge() ? 1 : -1;
    }
}

二进制搜索返回-1。

我得到的索引是:-1

当我反向排序时,我无法理解程序返回-1。有想法吗?

2 个答案:

答案 0 :(得分:1)

在Javadoc中查找binarySearch():

  

*“在进行此调用之前,必须根据指定的比较器(通过sort(List,Comparator)方法)将列表按升序排序。如果未排序,则结果不确定。      

...

     

返回:      搜索关键字的索引(如果包含在列表中);否则,((插入点)-1)。插入点定义为将键插入列表的点:第一个元素的索引大于键的索引,或者如果列表中的所有元素都为list.size()小于指定的键。请注意,这保证了当且仅当找到密钥时,返回值才> = 0。

由于一旦您更新比较器后,您的列表将按降序排序,则搜索算法会在错误的方向上“搜索”:


1)在“ 3”上搜索“土地”
2)评估2个compareTo 3
3)将搜索范围移到列表的左半部分(期望较小的数字在3的左侧)。
4)在“ 4”上搜索“土地”
5)评估2个compareTo 4个
6)未定义结果,列表似乎是无序的,因为在原始数字的左侧发现了一个较大的数字。

一旦发生这种情况,binarySearch的行为就好像该元素根本不在列表中。在这种情况下,它将返回(-(插入点)-1)。此处的插入点将被定义为索引0,因为第一个发现的值大于搜索的键的值是“ 5” @索引0:

index = (-(0) - 1)    
index = 0 - 1  
index = -1 

答案 1 :(得分:1)

Binary Search的工作前提是必须根据实现Comparator的方式来增加集合。换句话说,如果您使用Comparator以升序排序,则必须使用相同的Comparator进行搜索,类似地,如果您将Comparator反向,则必须对集合进行反向排序订购。因此,在您的情况下:

Collections.sort(personList, sortComp); // Collection is sorted in ascending order (p1 > p2)
Person p = new Person("2", "2", 2);
int indx = Collections.binarySearch(personList, p, searchComp); // Search with (p1 > p2)
System.out.println("index is:" + indx);

您正在按升序对集合进行排序(例如p1> p2),然后使用相同的比较器进行搜索,因此它可以工作。反转排序的那一刻,现在搜索将把集合减半,并在上半部分寻找更高的值。由于集合的顺序相反,因此不会找到任何集合并返回-1。

另一方面,在实现compare(a,b)时,该函数需要返回:

  • 如果a 小于0的值
  • 如果a == b,则等于0的值
  • 如果a> b
  • ,则该值大于零

因此,您可以p1.getAge() < p2.getAge() ? 1 : -1;来代替p1.getAge() - p2.getAge();

如果您使用的是 Java8 + ,则可以将searchPerson方法简化为:

private static void searchperson() {
    List<Person> personList = new ArrayList<>();
    for (int i = 1; i < 6; i++) {
        Person p = new Person(String.valueOf(i), String.valueOf(i), i);
        personList.add(p);
    }
    Collections.sort(personList, Comparator.comparingInt(Person::getAge));
    Person p = new Person("2", "2", 2);
    int indx = Collections.binarySearch(personList, p, Comparator.comparingInt(Person::getAge));
    System.out.println("index is:" + indx);
}