简体   繁体   English

用多个if-else语句(多个条件)优化函数的最佳方法是什么?

[英]What is the best way to optimize a function with many if-else statements(multiple conditions)?

What is the best approach or design pattern to optimize the code below?(I've thought about using switch statement but switch statement cannot handle multiple conditions in a single case.) 什么是优化下面代码的最佳方法或设计模式是什么?(我曾考虑过使用switch语句,但是switch语句无法在单个情况下处理多个条件。)

Below is the code snippet. 下面是代码片段。 Each major is determined by a certain numerical range. 每个专业都由某个数值范围确定。

public String getMajor(String major) {
    crnCompare = Integer.parseInt(major);
    if ((crnCompare >= 90702 && crnCompare <= 90733) || (crnCompare >= 10004 && crnCompare <= 10037)) {
        this.major = "AC";
    } else if ((crnCompare >= 10087 && crnCompare <= 10108) || (crnCompare >= 10471 && crnCompare <= 10482) || (crnCompare >= 90024 && crnCompare <= 90071)) {
        this.major = "CS";
    } else if ((crnCompare >= 10109 && crnCompare <= 10158) || (crnCompare >= 90072 && crnCompare <= 90116)) {
        this.major = "EC";
    } else if ((crnCompare >= 90117 && crnCompare <= 90203) || (crnCompare >= 10075 && crnCompare <= 10213) || (crnCompare >= 10498 && crnCompare <= 10572)) {
        this.major = "EN";
    } else if ((crnCompare >= 10038 && crnCompare <= 10040) || (crnCompare >= 10214 && crnCompare <= 10255) || (crnCompare >= 10256 && crnCompare <= 10260) || (crnCompare >= 90017 && crnCompare <= 90203) || crnCompare == 11172) {
        this.major = "FI";
    } else if ((crnCompare >= 90670 && crnCompare <= 90790) || (crnCompare >= 11236 && crnCompare <= 11239)) {
        this.major = "FS";
    } else if ((crnCompare >= 90253 && crnCompare <= 90273) || (crnCompare >= 90734 && crnCompare <= 90769) || (crnCompare >= 90274 && crnCompare <= 90360) || (crnCompare >= 10261 && crnCompare <= 10393)) {
        this.major = "GB";
    } else if ((crnCompare >= 100394 && crnCompare <= 10429) || (crnCompare >= 90361 && crnCompare <= 90398)) {
        this.major = "GLS";
    } else if ((crnCompare >= 10430 && crnCompare <= 10451) || (crnCompare >= 90399 && crnCompare <= 90420)) {
        this.major = "HI";
    } else if ((crnCompare >= 10452 && crnCompare <= 10468) || (crnCompare >= 90422 && crnCompare <= 90436) || crnCompare == 11119) {
        this.major = "IDCC";
    } else if ((crnCompare >= 9437 && crnCompare <= 90438) || (crnCompare >= 10469 && crnCompare <= 10470)) {
        this.major = "IPM";
    } else if ((crnCompare == 90421) || (crnCompare >= 11280 && crnCompare <= 11426)) {
        this.major = "ID";
    } else if ((crnCompare >= 90439 && crnCompare <= 90448) || (crnCompare >= 90483 && crnCompare <= 90497)) {
        this.major = "LTF";
    } else if ((crnCompare >= 90504 && crnCompare <= 90535) || (crnCompare >= 10573 && crnCompare <= 10596) || crnCompare == 90785) {
        this.major = "MG";
    } else if ((crnCompare >= 90536 && crnCompare <= 90553) || (crnCompare >= 10598 && crnCompare <= 10616) || crnCompare == 10740) {
        this.major = "MK";
    } else if ((crnCompare >= 90449 && crnCompare <= 90503) || (crnCompare >= 10514 && crnCompare <= 10564) || (crnCompare == 11120) || (crnCompare == 10555) || (crnCompare == 11127)) {
        this.major = "MA";
    } else if ((crnCompare >= 10637 && crnCompare <= 10715) || (crnCompare == 11142) || (crnCompare == 10739) || (crnCompare >= 90575 && crnCompare <= 90622)) {
        this.major = "NAS";
    } else if (crnCompare >= 90554 && crnCompare <= 90574 || crnCompare == 10617 || crnCompare == 10636) {
        this.major = "ML";
    } else if ((crnCompare >= 90623 && crnCompare <= 10646) || (crnCompare >= 10671 && crnCompare <= 10696)) {
        this.major = "PI";
    } else if ((crnCompare == 90647 || crnCompare == 90649) || (crnCompare >= 10697 && crnCompare <= 10698) || crnCompare == 10756) {
        this.major = "PRS";
    } else if ((crnCompare >= 11341 && crnCompare <= 11420)) {
        this.major = "SL";
    } else if ((crnCompare >= 90650 && crnCompare <= 90668) || (crnCompare >= 10716 && crnCompare <= 10734)) {
        this.major = "SO";
    } else if ((crnCompare == 10735)) {
        this.major = "ST";
    }

    return this.major;
}

You might want to look into Guava's RangeMap classes (other similar implementations are available). 您可能想研究Guava的RangeMap类(其他类似的实现方式也可用)。

These allow you to express these conditions something like this: 这些使您可以表达这些条件,如下所示:

RangeMap<Integer, String> rangeMap =
  ImmutableRangeMap.<Integer, String>builder()
    .put(Range.closed(90702, 90733), "AC")
    .put(Range.closed(10004, 10037), "AC")
    .put(Range.closed(10087, 10108), "EN")
    .put(Range.closed(10004, 10037), "AC")
    // ...
    .build();

Construct this once, and then query it like: 构造一次,然后查询如下:

String major = rangeMap.get(crmCompare);

There are a couple of advantage of this: 有两个优点:

  • It's a more compact syntax 这是一种更紧凑的语法
  • The creation of the ranges is validated, both for min and max being in the correct order, and for not overlapping. 验证了范围的创建,最小和最大均处于正确的顺序,并且没有重叠。

The disadvantage is the addition of Guava, if you're not already using it. 缺点是添加了番石榴,如果您还没有使用过的话。

Standard table-driven approach: 标准表驱动方法:

public static class Range
{
    // getters omitted for conciseness
    int low;
    int high;
    String major;
    public Range(int low, int high, String major)
    {
        this.low  = low;
        this.high  = high;
        this.major = major;
    }
    public boolean contains(int v)
    {
        return (v >= low && v <= high);
    }
}

public static Range[] ranges = {
        new Range(10004,10037,"AC"),
        new Range(10087,10108,"AC"),
        // etc
        // Ideally this table is populated from a data file that can
        // be updated at runtime without recompiling the code.
};

public String getMajor(String m)
{
    int crnCompare = Integer.parseInt(m);
    // Search for the matching range
    for (Range r : ranges)
        if (r.contains(crnCompare)) return r.major;
    return null;
}

Encode all the conditions as a low-high pair and corresponding major code, and put all the conditions into an array. 将所有条件编码为低-高对和相应的主代码,然后将所有条件放入数组中。 To determine the major search the array to find the matching condition. 要确定主要搜索数组,以找到匹配条件。

Possible enhancements (left as an exercise) include 可能的增强(保留为练习)包括

  1. loading the table from a file instead of hardcoding it 从文件加载表,而不是对其进行硬编码
  2. keeping the table sorted so you can do a binary search instead of a linear search. 保持表格排序,以便您可以执行二进制搜索而不是线性搜索。

I thought a while about this; 我想了一会儿。 focusing on the aspect of: "how can you make sure that your implementation is correct?" 关注以下方面:“如何确保实现正确?”

In that sense, I would have suggested a Range class, similar to the one proposed by Jim. 从这个意义上讲,我会建议一个Range类,类似于Jim提出的类。 Because such a class allows you to not only have a contains() method closely associated to the range data; 因为这样的类使您不仅可以拥有一个与范围数据紧密相关的contains()方法,而且还可以使您拥有一个与范围数据紧密相关的contains()方法。 but on top of that, you could add methods like: 但最重要的是,您可以添加以下方法:

  • boolean isOverlapping(Range other) ... which you can use to detect situations when you didn't pay attention and you defined ranges that, well, overlap! boolean isOverlapping(Range other) ...可以用来检测不注意的情况,并且您定义的范围可以重叠!
  • int compareTo(Range other) ... to implement the Comparable interface. int compareTo(Range other) ...实现Comparable接口。 And now you could sort ranges. 现在您可以范围进行排序 Which would allow you to do binary search when the list of ranges for a certain "category" gets too large. 当某个“类别”的范围列表太大时,这将允许您执行二进制搜索。

But: I would not put that "major" name into the Range class; 但是:我不会将“主要”名称放到Range类中。 but the other way round: 但是反过来:

public enum RangeBasedMajor {
  AC(Arrays.asList(new Range(90702, 90733), new Range(...)),
  EN(...

  private RangeBasedMajor(List<Range> ranges) ...

Key points here: 这里的重点:

  • you have one place where those "majors" are created and mapped to their ranges 您在一个地方创建了这些“专业”并将其映射到其范围
  • you can write some nice unit tests that fetch all constants from that enum, then grep their ranges, to ensure that no range is overlapping 您可以编写一些不错的单元测试,以从该枚举中获取所有常量,然后grep其范围,以确保没有范围重叠
  • and you are still free to push all that information into the special type of map Andy recommended to you in his solution ... 而且您仍然可以将所有信息随意放入Andy在其解决方案中推荐给您的特殊地图类型中...

Depending on your context, this might be overkill. 根据您的情况,这可能会过分杀伤力。 It really depends on how often you expect things to change in there; 这实际上取决于您期望事物在其中发生变化的频率。 and for example: if it is required to access/deal with that "major" information within other parts of the program. 例如:如果需要访问/处理程序其他部分中的“主要”信息。 When your requirement is only to map that number to a string (for example for printing something); 当您只需要将该数字映射到字符串时(例如用于打印内容); then that RangeMap is fine. 那么RangeMap就可以了。

But when you have other code that "deals" with "Majors" - then it might be worth looking into some of the suggestions I am making here. 但是,当您有其他代码与“专业”打交道时,那么值得在这里提出一些建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM