优化排序方法

时间:2017-07-03 03:20:22

标签: java sorting comparator

我有一个程序需要按以下顺序对匹配进行排序(以下是一个简短示例):

PIT  
PREDICTIONS  
Match 1  
Match 2  
Quarters 1 Match 1  
Quarters 1 Match 2  
Quarters 2 Match 1  
Semis 1  
Semis 2  
Finals 1  
Finals 2

注意:可以有无限的比赛,仅限1/4(无限子比赛),无限半决赛和无限决赛。

我有以下方法为上述标题之一分配分数:

public static long getMatchScore(String name) {
    long score = 0;
    String matchName = name.toLowerCase();
    String[] tokens = matchName.split("\\s+");

    if(matchName.startsWith("pit")) score -= 100000;
    else if(matchName.startsWith("predictions")) score -= 1000;
    else if(matchName.startsWith("quals")) score = Integer.parseInt(matchName.split("\\s+")[1]);
    else if(matchName.startsWith("quarters")) {
        if(Integer.parseInt(tokens[1]) == 1) score += 1000;
        else if(Integer.parseInt(tokens[1]) == 2) score += 10000;
        else if(Integer.parseInt(tokens[1]) == 3) score += 100000;
        else if(Integer.parseInt(tokens[1]) == 4) score += 1000000;

        score += Integer.parseInt(tokens[3]);
    }
    else if(matchName.startsWith("semis")) {
        if(Integer.parseInt(tokens[1]) == 1) score += 10000000;
        else if(Integer.parseInt(tokens[1]) == 2) score += 100000000;

        score += Integer.parseInt(tokens[3]);
    }
    else if(matchName.startsWith("finals")) {
        score += 1000000000; 
        score += Integer.parseInt(tokens[1]);
    }
    return score;
}

然后使用Java方法compareTo()对其进行排序。有没有更好的方法来做到这一点,而不是分配像100000000这样的大量数字。

3 个答案:

答案 0 :(得分:2)

这个要求将代码中的逻辑嵌入到Comparable类中。

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

public class MatchDescription implements Comparable<MatchDescription> {

    private MatchType matchType;
    private int matchOrder = 0;
    private int subMatchOrder = 0;

    public MatchDescription(String name) {
        String matchName = name.toLowerCase().trim();
        String[] tokens = matchName.split("\\s+");

        matchType = MatchType.getByName(tokens[0]);

        if (matchType.hasMatchOrder()) {
            matchOrder = Integer.parseInt(tokens[1]);
        }
        if (matchType.hasSubmatches()) {
            subMatchOrder = Integer.parseInt(tokens[3]);
        }
    }

现在,使用匹配顺序,子顺序等逻辑覆盖方法compareTo(T)和equals(Object)。

    @Override
    public int compareTo(MatchDescription other) {
        if (this.matchType == other.matchType)
            if (this.matchOrder == other.matchOrder)
                return (this.subMatchOrder - other.subMatchOrder);
            else
                return (this.matchOrder - other.matchOrder);
        else
            return (this.matchType.getMatchTypeOrder() - other.matchType.getMatchTypeOrder());  
    }

    @Override
    public boolean equals(Object other) {
        return (other instanceof MatchDescription) &&
                (((MatchDescription) other).matchType == this.matchType) &&
                (((MatchDescription) other).matchOrder == this.matchOrder);
    }

匹配类型通过内部枚举来管理:

    private enum MatchType {
        PIT("PIT",                 1, false, false),
        PREDICTIONS("PREDICTIONS", 2, false, false),
        MATCH("Match",             3, true,  false),
        QUARTERS("Quarters",       4, true,  true),
        SEMIS("Semis",             5, true,  true),
        FINALS("Finals",           6, true,  false)
        ;

        private String name;
        private int matchTypeOrder;
        private boolean hasMatchOrder;
        private boolean hasSubmatches;

        MatchType(String name, int matchTypeOrder, boolean hasMatchOrder, boolean hasSubmatches) {
            this.name           = name;
            this.matchTypeOrder = matchTypeOrder;
            this.hasMatchOrder  = hasMatchOrder;
            this.hasSubmatches  = hasSubmatches;
        }

        public boolean hasMatchOrder() {
            return hasMatchOrder;
        }

        public boolean hasSubmatches() {
            return hasSubmatches;
        }

        public static MatchType getByName(String matchName) {
            for (MatchType value : values()) {
                if (value.getName().equalsIgnoreCase(matchName))
                    return value;
            }
            return null;
        }

        private String getName() {          
            return name;
        }

        public int getMatchTypeOrder() {            
            return matchTypeOrder;
        }
    }

最后,toString()很好地呈现了对象:

public String toString() {
        String description = matchType.getName();
        if (matchType.hasMatchOrder()) {
            description +=  " " + matchOrder;

            if (matchType.hasSubmatches()) 
                description +=  " Match " + subMatchOrder;
        }

        return description;
    }

这是关于如何使用可比较对象的示例(注意还处理了空格):

    public static void main(String[] args) {
        String[] inputs = new String[] {
                "PIT  ",
                "       Finals 1  ",
                "PREDICTIONS  ",
            "   Match 2  ",
            "   Quarters 1 Match 1  ",
            "   Quarters 1 Match 2  ",          
            "   Match 1  ",
        "       Semis 1  Match 1 ",
        "       Semis 2   Match 1",
        "       Finals 2",
        "   Quarters 2 Match 1  "};

        List<MatchDescription> c = new ArrayList<>();
        for (String input : inputs) {
            c.add(new MatchDescription(input));
        }
        Collections.sort(c);

        for (MatchDescription e : c) {
            System.out.println(e);
        }

    }
}

答案 1 :(得分:1)

首先,我并没有讽刺地说,如果你要编写像getMatchScore这样的代码,你应该花更多的时间学习如何编写干净的代码,而不是实际掏出更多代码。该方法的cyclomatic complexity是最重要的。

也就是说,如果我们认识到您的输入具有固有权重的组,例如Match&lt; Quarters,我们应该使用Enum在代码中捕获它。一个具有整数权重的简单的就足够了,不需要像“隔壁的Java Guy”所示的复杂的一个。我测试过的以下代码如何工作?

private static final Comparator<String> BY_GROUP = (s1, s2) -> {
    Map.Entry<Integer, Integer> e1 = toTuple(s1);
    Map.Entry<Integer, Integer> e2 = toTuple(s2);

    Comparator<Map.Entry<Integer, Integer>> c = Comparator.<Map.Entry<Integer, Integer>, Integer>comparing(Map.Entry::getKey)
            .thenComparing(Map.Entry::getValue);

    return c.compare(e1, e2);
};

private static Map.Entry<Integer, Integer> toTuple(String s) {
    String[] tokens = s.toUpperCase().split("\\s");
    Group group = Group.valueOf(tokens[0]);
    int num = tokens.length > 1 ? Integer.valueOf(tokens[1]) : Integer.MIN_VALUE;

    return new AbstractMap.SimpleImmutableEntry<>(group.weightage(), num);
}

private enum Group {
    PIT(0), PREDICTIONS(1), MATCH(2), QUARTERS(3), SEMIS(4), FINALS(5);

    private int weightage;

    Group(int weightage) {
        this.weightage = weightage;
    }

    public int weightage() {
        return this.weightage;
    }
}

public static void main(String[] args) {
    List<String> input = Arrays.asList("PIT", "PREDICTIONS", "Match 1", "Match 2",
            "Quarters 1 Match 1", "Quarters 1 Match 2", "Quarters 2 Match 1", "Semis 1", "Semis 2",
            "Finals 1", "Finals 2");

    input.sort(BY_GROUP);
    System.out.println(input);
}

答案 2 :(得分:0)

第一件事:给定的代码确实包含重复的代码。这本身就是一件坏事。但也是一个小的性能打击。

示例:您正在调用parseInt(token [0])5次或更多次。您可以重构整个方法以使其更容易阅读和B)例如仅计算一次表达式。