带有密钥提取器的二进制搜索Java列表

时间:2019-01-17 21:15:24

标签: java

说我有

class Item {
   int id;
   int type;
}

我可以这样做:

List<Item> items;
Item itemToFind;
Comparator<Item> itemComparator;
Collections.binarySearch(items, itemToFind, itemComparator);

但是,我只给出了对象的一个​​属性,而不是整个对象,例如上述示例中的type。假设列表按该属性排序,那么Java或某些已建立的库中是否有标准方法可以实现此目的:

List<Item> items;
Function<Item, Integer> typeExtractor = Item::getType;
int typeToFind;
Comparator<Integer> typeComparator = Integer::compare;
binarySearch(items, typeExtractor, typeToFind, typeComparator);

没有额外的开销(例如,将List<Item>转换为List<Integer>以调用Collections.binarySearch或类似的费用?

2 个答案:

答案 0 :(得分:1)

比较器仍为Comparator<Item>。您将要更改的是比较器的实现,以便根据类型而不是id进行评估。

Comparator<Item> comparator = new Comparator<Item>(){
    public int compare(Item a, Item b) 
    { 
        return a.getType() - b.getType(); 
    } 
}

Item,需要将类型的吸气剂或属性设为公开。如果使用ID,则相同。

  

但是,不确定您如何建议我打电话   Collections.binarySearch

用法没有改变(改变的是比较器对象内部的比较方式):

Item itemToFind = new Item();
itemToFind.setType(typeToFind);
Collections.binarySearch(items, itemToFind, comparator );

对此主题进行了一些思考之后:

使用Item作为针头的另一种方法是将Comparator放在Item和针头工具的接口上。​​

返回int值的接口:

 public interface Intgettable{
     public int getInt();
 }

Item应该必须实现此接口:

public class Item implements Intgettable{
     private int id;
     private int type;

     public void setId(int id){ 
         this.id = id;
     }
     public void setType(int type){ 
         this.type = type;
     }
     public int getId(){
         return id;
     }
     public int getType(){
         return type;
     }
     public int getInt(){
         return type;
     }
 }

要搜索的键是可以创建的Intgettable

1-使用扩展Intgettable的类。

public static class MyItemKey implements Intgettable{
     private int value;

     public MyItemKey(int v){
         this.value = v;
     }

     @Override
     public int getInt(){
         return value;
     }
}

MyItemKey typeToFind = new MyItemKey(6);

2-作为方法内部的匿名类。

Intgettable typeTofind = new Intgettable(){
        private int value = 6;
        public int getInt(){
            return value;
        }
};

3-或使用lambda版本:

Intgettable typeTofind = ()->{return 6;};

Comparator将是:

Comparator<Intgettable> comparator = new Comparator<Intgettable>(){
        public int compare(Intgettable a, Intgettable b){
            return a.getInt() - b.getInt();
        }
};

最后在二进制搜索中使用它:

Collections.binarySearch(items, typeToFind, comparator );

答案 1 :(得分:0)

您的问题是,在Collection<T>中,java的二进制搜索实现仅允许搜索T类型的项目。为了搜索属于T的另一种类型,您可以执行以下操作:

  1. 将另一种类型包装在T内,您的情况应如下所示:
List<Item> items;
int typeToFind;

Item itemToFind = new Item(/* random value for id */ 0, typeToFind);
int index = binarySearch(items, itemToFind , (a, b) -> a.getType() - b.getType());

在此处添加一些重要说明:

     -项的比较应仅且仅取决于“类型”,否则您可能会遇到一些讨厌的错误;
    -项目列表应排序。排序应仅且仅取决于“类型”(基本上使用与以前相同的比较器)
  1. 从初始列表创建新列表:
List<Items> items;
int typeToFind

int index = binarySearch(items.stream.map(item -> item.getType()).collect(Collectors.toList()), itemToFind);

据我所知,Java的标准库不提供带有用于键相等性的比较器的二进制搜索实现。如果这些选项不能满足您的要求,则可能应该搜索库或实施自己的搜索。