4

我有一个类和实例列表,看起来像这样(更改了字段名称以保护无辜/专有):

public class Bloat
{
    public long timeInMilliseconds;
    public long spaceInBytes;
    public long costInPennies;
}

public class BloatProducer
{
    final private List<Bloat> bloatList = new ArrayList<Bloat>();
    final private Random random = new Random();
    public void produceMoreBloat()
    {
       int n = bloatList.size();
       Bloat previousBloat = (n == 0) ? new Bloat() : bloatList.get(n-1);
       Bloat newBloat = new Bloat();
       newBloat.timeInMilliseconds = 
          previousBloat.timeInMilliseconds + random.nextInt(10) + 1;
       newBloat.spaceInBytes = 
          previousBloat.spaceInBytes + random.nextInt(10) + 1;
       newBloat.costInPennies = 
          previousBloat.costInPennies + random.nextInt(10) + 1;
       bloatList.add(newBloat);
    }
    /* other fields/methods */

    public boolean testMonotonicity()
    {
    Bloat previousBloat = null;
    for (Bloat thisBloat : bloatList)
            {
               if (previousBloat != null)
               {
                  if ((previousBloat.timeInMilliseconds 
                     >= thisBloat.timeInMilliseconds)
                   || (previousBloat.spaceInBytes 
                     >= thisBloat.spaceInBytes)
                   || (previousBloat.costInPennies
                     >= thisBloat.costInPennies))
                       return false;
               }
               previousBloat = thisBloat;
           }
           return true;
    }

BloatProducer bloatProducer;

该列表bloatList由内部保存BloatProducer并以这样的方式维护:它只附加新Bloat记录,不修改任何旧记录,并且每个字段都是单调递增的,例如bloatProducer.testMonotonicity()总是返回true

我想使用timeInMilliseconds、spaceInBytes 或 costInPennies 字段Collections.binarySearch(list,key,comparator)来搜索记录。Bloat(如果数字在两条记录之间,我想找到上一条记录)

编写一系列 3 个比较器类以使其工作的最简单方法是什么?我是否必须使用带有虚拟字段的 Bloat 对象作为我不搜索的键?

4

5 回答 5

5

您需要为要比较的每个字段编写一个单独的比较器:

public class BloatTimeComparator implements Comparator<Bloat> {
    public int compare(Bloat bloat1, Bloat bloat2) {
        if (bloat1.timeInMilliseconds > bloat2.timeInMilliseconds) {
            return 1;
        } else if (bloat1.timeInMilliseconds < bloat2.timeInMilliseconds) {
            return -1;
        } else {
            return 0;
        }
    }
}

对于要比较的每个属性,依此类推Bloat(您需要为每个属性创建一个比较器类)。然后使用 Collections 辅助方法:

Collections.binarySearch(bloatList,  bloatObjectToFind, 
    new BloatTimeComparator());

从 binarySearch 方法的Java 文档中,返回值将是:

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

这是您指定的您想要的索引。

于 2009-07-15T22:13:38.050 回答
2

Comparator如果要按 3 个属性中的每一个进行搜索,则需要 3 个单独的 s。

一个更简洁的选择是有一个泛型Comparator,它接收一个参数,告诉它通过哪个字段进行比较。

一个基本的通用比较器应该是这样的:

public class BloatComparator implements Comparator<Bloat>
{
    CompareByEnum field;

    public BloatComparator(CompareByEnum field) {
        this.field = field;
    }

    @Override
    public int compare(Bloat arg0, Bloat arg1) {
        if (this.field == CompareByEnum.TIME){
            // compare by field time
        }
        else if (this.field == CompareByEnum.SPACE) {
            // compare by field space
        }
        else {
            // compare by field cost
        }
    }
}
于 2009-07-15T22:03:50.987 回答
1

这是编写第一个比较器的测试驱动方法:

public class BloatTest extends TestCase{
    public class Bloat {
        public long timeInMilliseconds;
        public long spaceInBytes;
        public long costInPennies;

        public Bloat(long timeInMilliseconds, long spaceInBytes, long costInPennies) {
            this.timeInMilliseconds = timeInMilliseconds;
            this.spaceInBytes = spaceInBytes;
            this.costInPennies = costInPennies;
        }
    }

    public void testMillisecondComparator() throws Exception {
        Bloat a = new Bloat(5, 10, 10);
        Bloat b = new Bloat(3, 12, 12);
        Bloat c = new Bloat(5, 12, 12);

        Comparator<Bloat> comparator = new MillisecondComparator();
        assertTrue(comparator.compare(a, b) > 0);
        assertTrue(comparator.compare(b, a) < 0);
        assertEquals(0, comparator.compare(a, c));
    }

    private static class MillisecondComparator implements Comparator<Bloat> {
        public int compare(Bloat a, Bloat b) {
            Long aTime = a.timeInMilliseconds;
            return aTime.compareTo(b.timeInMilliseconds);
        }
    }


}
于 2009-07-15T22:18:56.713 回答
0

如果您想利用对所有三个属性的二分搜索,您必须为它们创建比较器,并具有按比较器排序的附加列表或树集。

于 2009-07-15T22:01:44.377 回答
0

测试程序 ( MultiBinarySearch.java) 以查看这些想法是否正常工作(它们似乎):

package com.example.test;

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

class Bloat
{
    final public long timeInMilliseconds;
    final public long spaceInBytes;
    final public long costInPennies;
    static final private int N = 100; 
    public Bloat(long l1, long l2, long l3) {
        timeInMilliseconds = l1;
        spaceInBytes = l2;
        costInPennies = l3; 
    }
    public Bloat() { this(0,0,0); }
    public Bloat moreBloat(Random r)
    {
        return new Bloat(
                timeInMilliseconds + r.nextInt(N) + 1,
                spaceInBytes + r.nextInt(N) + 1,
                costInPennies + r.nextInt(N) + 1
        );
    }
    public String toString() {
        return "[bloat: time="+timeInMilliseconds
            +", space="+spaceInBytes
            +", cost="+costInPennies
            +"]";
    }

    static int compareLong(long l1, long l2)
    {
        if (l2 > l1)
            return -1;
        else if (l1 > l2)
            return 1;
        else
            return 0;
    }

    public static class TimeComparator implements Comparator<Bloat> {
        public int compare(Bloat bloat1, Bloat bloat2) {
            return compareLong(bloat1.timeInMilliseconds, bloat2.timeInMilliseconds);
        }
    }
    public static class SpaceComparator implements Comparator<Bloat> {
        public int compare(Bloat bloat1, Bloat bloat2) {
            return compareLong(bloat1.spaceInBytes, bloat2.spaceInBytes);
        }
    }
    public static class CostComparator implements Comparator<Bloat> {
        public int compare(Bloat bloat1, Bloat bloat2) {
            return compareLong(bloat1.costInPennies, bloat2.costInPennies);
        }
    }
    enum Type { 
        TIME(new TimeComparator()), 
        SPACE(new SpaceComparator()),
        COST(new CostComparator());

        public Comparator<Bloat> comparator;
        Type(Comparator<Bloat> c) { this.comparator = c; } 
    } 
}

class BloatProducer
{
    final private List<Bloat> bloatList = new ArrayList<Bloat>();
    final private Random random = new Random();
    public void produceMoreBloat()
    {
        int n = bloatList.size();
        Bloat newBloat = 
            (n == 0) ? new Bloat() : bloatList.get(n-1).moreBloat(random);
            bloatList.add(newBloat);
    }
    /* other fields/methods */

    public boolean testMonotonicity()
    {
        Bloat previousBloat = null;
        for (Bloat thisBloat : bloatList)
        {
            if (previousBloat != null)
            {
                if ((previousBloat.timeInMilliseconds 
                        >= thisBloat.timeInMilliseconds)
                    || (previousBloat.spaceInBytes 
                        >= thisBloat.spaceInBytes)
                    || (previousBloat.costInPennies
                        >= thisBloat.costInPennies))
                    return false;
            }
            previousBloat = thisBloat;
        }
        return true;
    }
    public int searchBy(Bloat.Type t, Bloat key)
    {
        return Collections.binarySearch(bloatList, key, t.comparator);
    }
    public void showSearch(Bloat.Type t, Bloat key)
    {
        System.out.println("Search by "+t+": "); 
        System.out.println(key);
        int i = searchBy(t,key);
        if (i >= 0)
        {
            System.out.println("matches");
            System.out.println(bloatList.get(i));
        }
        else
        {
            System.out.println("is between");
            i = -i-1;
            Bloat b1 = (i == 0) ? null : bloatList.get(i-1);
            System.out.println(b1);
            Bloat b2 = (i >= bloatList.size()) ? null : bloatList.get(i);
            System.out.println("and");
            System.out.println(b2);
        }
    }
}

public class MultiBinarySearch {
    private static int N = 1000;
    public static void main(String[] args)
    {
        BloatProducer bloatProducer = new BloatProducer();
        for (int i = 0; i < N; ++i)
        {
            bloatProducer.produceMoreBloat();
        }

        System.out.println("testMonotonicity() returns "+
                bloatProducer.testMonotonicity());
        Bloat key;
        key = new Bloat(10*N, 20*N, 30*N);
        bloatProducer.showSearch(Bloat.Type.COST, key);
        bloatProducer.showSearch(Bloat.Type.SPACE, key);
        bloatProducer.showSearch(Bloat.Type.TIME, key);
        key = new Bloat(-10000, 0, 1000*N);
        bloatProducer.showSearch(Bloat.Type.COST, key);
        bloatProducer.showSearch(Bloat.Type.SPACE, key);
        bloatProducer.showSearch(Bloat.Type.TIME, key);
    }
}
于 2009-07-15T22:49:01.550 回答