Search code examples
javabinary-search

How can I implement binary search in Substring in function and two values in Comparator?


I have a problem about implementing binary search in substring. In my City object, there is a cityName variable as defined String. I want to enter any substring like "Sha" and it shows the result of "Sha". Except for this, there is a weight variable to sort descend city names. For example, the bigger weight value is at the top and the sort process is based on descending.

How can I do that? How can I add this to the comparater area?

Here is my City Object

public class City implements Serializable{
    private Integer cityWeight;
    private String cityName;
}

Here is my code snippet shown below.

// Not implement Binary Search

public static Integer[] searchByCharacters(List<City> list, String sub) {
        List<Integer> result = new ArrayList<>();
        Collections.sort(list);
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getCityName().contains(sub))
                result.add(i);
        }
        return result.stream().toArray(Integer[]::new);
}

I want to implement binary search above the code snippet but it doesn't work.

public static Integer[] searchBySubStringCharacter(List<City> list, String sub) {
        List<Integer> result = new ArrayList<>();

        Comparator<City> comparator = new Comparator<City>() {
            public int compare(City node1, City node2) {
                boolean node1Contains = node1.getCityName().contains(sub);
                boolean node2Contains = node2.getCityName().contains(sub);
                if (node1Contains && !node2Contains) {
                    return 1;
                } else if (!node1Contains && node2Contains ) {
                    return -1;
                } else {
                    return 0;
                }
            }
        };

        Collections.sort(list, comparator);
        for (int i = 0; i < list.size(); i++) {
            int index = Collections.binarySearch(list, new City(sub), comparator);
            if (index >= 0)
                result.add(i);
        }

        return result.stream().toArray(Integer[]::new);
    }

This code shows all city list and the result of binary search. How can I extract all city list from the result?


Solution

  • binarySearch accepts the key to be searched for having type the class of the objects in the list,

    public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
    

    So pass City object in binarySearch,

    for (int i = 0; i < list.size(); i++) {
         int index = Collections.binarySearch(list, new City(sub), comparator);
         if (index >= 0)
            result.add(i);
    }
    

    Also if you are using java8+, you can use lambda for Comparator,

    Comparator<City> comparator = (node1, node2) -> {
         boolean node1Contains = node1.getCityName().contains(sub);
         boolean node2Contains = node2.getCityName().contains(sub);
         if (node1Contains && !node2Contains) {
            return 1;
         } else if (!node1Contains && node2Contains ) {
            return -1;
         } else {
            return 0;
         }
    };
    

    Update: To sort based on the weight in case of a match you need to use Integer.compare(node2.getCityWeight(), node1.getCityWeight()),

    Also iyou can not binarySearch with same comparator as you don't know the weight of the city you are searching. you can use Stream,

    public static Integer[] searchBySubStringCharacter(List<City> list, String sub) {
      List<Integer> result = new ArrayList<>();
    
      Comparator<City> comparator = (node1, node2) -> {
         boolean node1Contains = node1.getCityName().contains(sub);
         boolean node2Contains = node2.getCityName().contains(sub);
         if (node1Contains && !node2Contains) {
            return 1;
         } else if (!node1Contains && node2Contains ) {
            return -1;
         } else {
            return Integer.compare(node2.getCityWeight(), node1.getCityWeight());
         }
      };
      Collections.sort(list, comparator);
    
      return IntStream.rangeClosed(0, list.size() - 1)
              .filter(i -> list.get(i).getCityName().contains(sub))
              .boxed()
              .toArray(Integer[]::new);
    }