2つのバージョンで処理を分岐させたいときの、バージョン文字列の比較（Java実装）

タスク管理サービスのバージョンに応じてシステムの処理を変えたいということがあったので、2つのバージョン文字列の比較を行う実装をしてみました。バージョンを文字列として扱い、ピリオドで分割してから、それぞれの部分をInteger変換し比較する方法になります。

## やりたいこと

- 例、あるサービスのバージョンが2.5以上であるときと、2.5未満であるときで処理を分岐させたい。
- バージョンは、2.5.0のようにx.y.zのピリオドで区切られた3つの部分を考慮する必要がある。
- バージョンの表記には、1.3-SNAPSHOT,1.3aのような書き方も世の中にあるが、今回は考慮しない。
- 分岐点になるバージョンは固定で決まっており、サービスのバージョンの文字列は設定ファイルから読み込む。

## 実装

実装は[このサイト](https://stackoverflow.com/questions/198431/how-do-you-compare-two-version-strings-in-java)を参考に書きました。
より詳しい議論がなされているので、興味のある方は参照してみてください。

hashCodeの計算については自分で実装してみました。（これでいいかは不明）

```java:Version.java
package com.example;

import java.util.Arrays;
import java.util.ArrayList;

public class Version implements Comparable<Version> {
    
        private String version;
    
        private String[] parts;
    
        public final String getVersion() {
            return this.version;
        }
    
        public String[] getParts() {
            return this.parts;
        }
    
        public Version(String version) {
            if(version == null)
                throw new IllegalArgumentException("Version can not be null");
            if(!version.matches("[0-9]+(\\.[0-9]+)*"))
                throw new IllegalArgumentException("Invalid version format");
            this.version = version;
            this.parts = this.getVersion().split("\\.");
        }
    
        @Override public int compareTo(Version that) {
            if(that == null)
                return 1;
            String[] thisParts = this.getParts();
            String[] thatParts = that.getParts();
            int length = Math.max(thisParts.length, thatParts.length);
            for(int i = 0; i < length; i++) {
                int thisPart = i < thisParts.length ?
                        Integer.parseInt(thisParts[i]) : 0;
                int thatPart = i < thatParts.length ?
                        Integer.parseInt(thatParts[i]) : 0;
                if(thisPart < thatPart)
                    return -1;
                if(thisPart > thatPart)
                    return 1;
            }
            return 0;
        }
    
        @Override public boolean equals(Object that) {
            if(this == that)
                return true;
            if(that == null)
                return false;
            if(this.getClass() != that.getClass())
                return false;
            return this.compareTo((Version) that) == 0;
        }
    
        @Override public final int hashCode() {
            ArrayList<Integer> intParts = new ArrayList();
            String[] thisParts = this.getParts();
            for(int i = 0; i < thisParts.length; i++) {
                intParts.add(Integer.parseInt(thisParts[i]));
            }
            Integer[] list = intParts.toArray(new Integer[intParts.size()]);
            return Arrays.hashCode(list);
        }
    }
```

main関数は以下のように試しに動かしてみました。

```java:Main.java
package com.example;

import com.example.Version;

public class Main {

    public static void main(String[] args) {
        Version a = new Version("1.1");
        Version b = new Version("1.1.1");
        System.out.println(a.compareTo(b)); // return -1 (a<b)
        System.out.println(a.equals(b));    // return false
        System.out.println(a.hashCode());
        System.out.println(b.hashCode());

        Version c = new Version("2.0");
        Version d = new Version("1.9.9");
        System.out.println(c.compareTo(d)); // return 1 (c>d)
        System.out.println(c.equals(d));   // return false
        System.out.println(c.hashCode());
        System.out.println(d.hashCode());

        Version e = new Version("2.06");
        Version f = new Version("2.060");
        System.out.println(e.compareTo(f));  // return -1 (e<f)
        System.out.println(e.equals(f));  // return false

        Version g = new Version("3.3.0");
        Version h = new Version("3.3.0");
        System.out.println(g.compareTo(h));  // return 0 (g==h)
        System.out.println(g.equals(h));  // return true
        System.out.println(g.hashCode());
        System.out.println(h.hashCode());
    }
}
```

```text:Output
-1
false
993
30784
1
false
1023
31040
-1
false
0
true
32767
32767
```

とりあえず、このやり方でしのげそうなのでいいやと思いました。
本当は、1.3-SNAPSHOT,1.3aのようなバージョンの書き方が入っていても使えるようにしたかったのですが。。。

# 参照・引用

- [How do you compare two version Strings in Java?](https://stackoverflow.com/questions/198431/how-do-you-compare-two-version-strings-in-java)