在Java中存储唯一排序值的最有效方法(最佳性能和最小gc)是什么?

时间:2014-02-02 12:00:03

标签: java sorting

在Java中存储唯一排序值的最有效方式(最佳性能和最小gc)是什么?

目前我使用HashMap获取一组唯一值,然后将HashMap值复制到ArrayList中,然后使用Collections.sort()对值进行排序,因为我的对象实现了Comparable,最终给出了一个唯一的排序值。代码将每秒运行数千次,因此我想要最好的方法。任何人都可以提出更好的选择吗?

/代码只能在一个线程中运行。

我的代码如下:

public class LapRanking {
    private final Map<String, Car> carsInRace = new HashMap<String,Car>();

    public List<Car> processLap(Car car){

        carsInRace.put(car.driverName, car);

        List<Car> lapTimeRankings = new ArrayList<Car>(carsInRace.values());

        Collections.sort(lapTimeRankings);

        return lapTimeRankings;
    }

    public static void main(String[] args) {
        Car one = new Car("DriverOne");
        Car two = new Car("DriverTwo");
        Car three = new Car("DriverThree");

        LapRanking lapRanking = new LapRanking();

        for (int i = 0; i < 100; i++) {
            one = one.setLapTime();
            two = two.setLapTime();
            three = three.setLapTime();

            lapRanking.processLap(one);
            lapRanking.processLap(two);
            lapRanking.processLap(three);
        }
    }
}

public class Car implements Comparable<Car> {
    private static final Random randomTestTimes = new Random();
    public final String driverName;
    public final double lapTime;
    public final double firstQuarterTime;

    public Car(String driverName) {
        this.driverName = driverName;
        this.lapTime = Double.MAX_VALUE;
        this.firstQuarterTime = Double.MAX_VALUE;
    }

    public Car(String driverName, double lapTime, double firstQuarterTime) {
        this.driverName = driverName;
        this.lapTime = lapTime;
        this.firstQuarterTime = firstQuarterTime;
    }

    public Car setLapTime(){
        return new Car(driverName,randomTestTimes.nextDouble(), randomTestTimes.nextDouble());
    }

    @Override
    public int compareTo(Car o) {
        int i = Double.compare(lapTime, o.lapTime);
        if(i !=0)
            return i;

        return Double.compare(firstQuarterTime, o.firstQuarterTime);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("Car {driverName='").append(driverName).append('\'');
        sb.append(", lapTime=").append(lapTime);
        sb.append(", firstQuarterTime=").append(firstQuarterTime).append('}');
        return sb.toString();
    }
}

3 个答案:

答案 0 :(得分:1)

据我所知,对于所写的应用程序,地图实际上没有用处。 IMO,性能最高的解决方案是用预先分配并在启动时填充的适当大小的Car[]替换地图。然后使用Arrays.sort()每圈进行一次就地排序。

满足您的要求。它存储唯一的对象(Car对象)并保持它们的排序。

应该在启动后没有生成垃圾,除了(可能)由sort(...)方法本身创建的垃圾......以及输出排名。

这样的事情:

    Car[] cars = new Car[] {one, two, three};

    for (int i = 0; i < 100; i++) {
        one.setLapTime();
        two.setLapTime();
        three.setLapTime();

        // Sort the cars based on their `compareTo` method; i.e. lap time.
        Arrays.sort(cars);

        // Output the cars, ranked by lap time
        for (Car car in cars) {
            ...
        }
    }

针对Car不可变的变体进行了更新:

    Car[] cars = new Car[3];

    for (int i = 0; i < 100; i++) {
        one = one.setLapTime();
        two = two.setLapTime();
        three = three.setLapTime();
        cars[0] = one;
        cars[1] = two;
        cars[2] = three;
        Arrays.sort(cars);
    }

或者......更简洁:

    Car[] cars = new Car[]{one, two, three};
    for (int i = 0; i < 100; i++) {
        for (j = 0; j < cars.length[]; j++) {
           cars[j] = cars[j].setLapTime();
        }
        Arrays.sort(cars);
    }

(虽然我必须说实际创建并返回一个新对象的setter是非常差的界面设计。如果我是代码审查该代码,那么就会有很多红色的写作。 。)


顺便说一句,使用TreeMap的想法没有用,因为你的算法实际上是在对地图的值集进行排序而不是键集。

答案 1 :(得分:1)

您应该考虑使用TreeMapTreeSet

http://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html
http://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html

它们可能非常适合您的情况。

答案 2 :(得分:0)

我还没有计时或做过任何基准测试,但我强烈怀疑你会通过消除processLap中ArrayList的“新”分配来获得最大的加速。内存分配比3项列表上的排序要贵得多。

如果在连续调用processLap之间没有添加任何汽车,那么实际上不需要再次将HashMap中的值复制到已分配的ArrayList中。只需对您已有的ArrayList进行排序。

public class LapRanking {
    private final Map<String, Car> carsInRace = new HashMap<String,Car>();
    ArrayList<Car> lapTimeRankings;

    public List<Car> processLap(Car car){

        Car oldcar = carsInRace.put(car.driverName, car);

        if (oldcar == null)
        {
            lapTimeRankings = new ArrayList<Car>(carsInRace.values());
        }

        Collections.sort(lapTimeRankings);

        return lapTimeRankings;
    }

正如其他人已经说过的那样 - 对哈希表的需求也是可疑的。

如果您可以使用单独的功能将汽车插入比赛,而不是重载processLap,那么您将会更好......

public class LapRanking {
    private final Map<String, Car> carsInRace = new HashMap<String,Car>();
    boolean isDirty;
    ArrayList<Car> lapTimeRankings;

    void insertCarIntoRace(Car car)
    {
        carsInRace.put(car.driverName, car);
        isDirty = true;
    }

    public List<Car> processLap(Car car){

        if (isDirty)
        {
            lapTimeRankings = new ArrayList<Car>(carsInRace.values());
            isDirty = false;
        }

        Collections.sort(lapTimeRankings);

        return lapTimeRankings;
    }

    public static void main(String[] args) {
        Car one = new Car("DriverOne");
        Car two = new Car("DriverTwo");
        Car three = new Car("DriverThree");


        LapRanking lapRanking = new LapRanking();
        lapRanking.insertCarIntoRace(car1);
        lapRanking.insertCarIntoRace(car2);
        lapRanking.insertCarIntoRace(car3);


        for (int i = 0; i < 100; i++) {
            one.setLapTime();
            two.setLapTime();
            three.setLapTime();

            lapRanking.processLap(one);
            lapRanking.processLap(two);
            lapRanking.processLap(three);
        }
    }
}