3

I have a text file containing the following strings (which are versions of a software):

1_10_2_0_154
3_10_5_2_10
2_10_4_1
3_10_5_1_37

I'm trying to find the most recent version, in this case 3_10_5_2_10 is the version that I'm trying to display using java.

For the moment, here is my code:

    BufferedReader br;
    String version;
    ArrayList<List<Integer>> array = new ArrayList<List<Integer>>();
    List<Integer> liste = new ArrayList<Integer>();

    try{
        br = new BufferedReader(new FileReader(new File(FILEPATH)));

        while((version= br.readLine()) != null)
        {
            liste = Arrays.asList(version.split("_")).stream().

    map(s -> Integer.parseInt(s.trim())).collect(Collectors.toList());

            array.add(liste);
        }

        for(int i = 0; i < array.size(); i++)
        {   
            for (List l: array)
            {
                Object z = l.get(i);
                List<Object> listes = new ArrayList<Object>();
                listes.add(z);
                System.out.println(listes);
            }               
        }
        br.close();

        System.out.println(array);
    }catch(FileNotFoundException e){
        e.printStackTrace();
    }catch(IOException e){
        e.printStackTrace();
    }      

I made a loop to save strings to ArrayList> like:

[[1,10,2,0,154] , [3,10,5,2,10], [2,10,4,1], [3,10,5,1,37]]

I want to get the elements of each list and compare them to find the most biggest one (most recent one) but I don't know to do that..

6
  • 5
    can't you just order them as string? Commented Mar 21, 2018 at 13:44
  • 2
    @Kepotx That wouldn't always work. Consider 10_1 and 9_1 Commented Mar 21, 2018 at 13:55
  • 1
    Try using ComparableVersion Commented Mar 21, 2018 at 13:56
  • @logger Wouldn't always work for the same reason as shown by user7 Commented Mar 21, 2018 at 13:57
  • 1
    Possible duplicate of How do you compare two version Strings in Java? Commented Mar 21, 2018 at 14:13

7 Answers 7

4

I sugguest you a object approach, define a class named Version with compareTo method, then using method sort on Collections class you can simply sort your versions.

Advantages

  • Clean and Clear code
  • Data validation

Main:

public class Main {

    public static void main(String[] args){

        List<Version> versions = Arrays.asList(
                Version.create("1_10_2_0_154"),
                Version.create("3_10_5_2_10"),
                Version.create("2_10_4_1_49"),
                Version.create("3_10_5_1_37"));

        versions.sort(Version::compareTo);

        System.out.println(versions.get(0).toString());
    }

}

Version:

public class Version implements Comparable<Version> {

    private final int major;
    private final int minor;
    private final int bug;
    private final int release;
    private final int build;

    public Version(int major, int minor, int bug, int release, int build) {
        this.major = major;
        this.minor = minor;
        this.bug = bug;
        this.release = release;
        this.build = build;
    }

    public int getMajor() {
        return major;
    }

    public int getMinor() {
        return minor;
    }

    public int getBug() {
        return bug;
    }

    public int getRelease() {
        return release;
    }

    public int getBuild() {
        return build;
    }

    @Override
    public String toString() {
        return "Version{" +
                "major=" + major +
                ", minor=" + minor +
                ", bug=" + bug +
                ", release=" + release +
                ", build=" + build +
                '}';
    }



    public static Version create(String value){

        String[] splitRes = value.split("_");
        List<Integer> intValues = new ArrayList<>();

        for(String v : splitRes){
            intValues.add(Integer.parseInt(v));
        }

        return create(intValues);
    }

    public static Version create(List<Integer> values){

        if(Objects.requireNonNull(values).size() < 5)
            throw new IllegalArgumentException();

        return new Version(
                values.get(0),
                values.get(1),
                values.get(2),
                values.get(3),
                values.get(4)
        );
    }


    @Override
    public int compareTo(Version that) {
        if (this.major > that.major) {
            return -1;
        } else if (this.major < that.major) {
            return 1;
        }

        if (this.minor > that.minor) {
            return -1;
        } else if (this.minor < that.minor) {
            return 1;
        }

        if (this.bug > that.bug) {
            return -1;
        } else if (this.bug < that.bug) {
            return 1;
        }

        if (this.release > that.release) {
            return -1;
        } else if (this.release < that.release) {
            return 1;
        }

        if (this.build > that.build) {
            return -1;
        } else if (this.build < that.build) {
            return 1;
        }
        return 0;
    }
}

UPDATE 1

As suggested by @Henrik i updated the list sorting with a Java 8 approach.

UPDATE 2

I reversed the compareTo method so now you can simply do plain sort calling sort method on list and passing method reference Version::compareTo

UPDATE 3

A more dynamic solution for Version class:

public class Version implements Comparable<Version> {

    private final List<Integer> values;

    public Version(List<Integer> values) {
        this.values = values;
    }

    public List<Integer> getValues() {
        return values;
    }

    @Override
    public String toString() {

        return String.join("_", values
                .stream()
                .map(Object::toString)
                .collect(Collectors.toList()));
    }

    @Override
    public int compareTo(Version that) {

        List<Integer> thatValues = that.getValues();

        for(int index = 0; index < values.size(); index++){

            Integer value = values.get(index);
            Integer thatValue = thatValues.get(index);

            if (value > thatValue) {
                return -1;
            } else if (value < thatValue) {
                return 1;
            }
        }

        return 0;
    }


    public static Version create(String value){

        String[] splitRes = value.split("_");
        List<Integer> intValues = new ArrayList<>();

        for(String v : splitRes){
            intValues.add(Integer.parseInt(v));
        }

        return new Version(intValues);
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

I agree with this approach. Consider implementing the comparison using the utility methods added to the Comparator interface in Java 8, comparingInt and thenComparingInt.
i updated my answer with your sugguest, now i'm using Comparator.reverseOrder() to sort the list
Is it possible to make this code work if I have versions such as "3_10_0_4_53_15" instead of "3_10_0_4_53" ?
Will this list grow in the future ? You can add a new field and all will work but if the list is dynamic it takes a different solution(more flessible)
Yes the lenght of the versions can change like "3_10_0_4_53_15", "3_10_0_4_53" and even "3_10_0_4", your code seems to work perfectly with a fixed lenght of string, is it possible to make it more flexible as you said ?
|
1

You can write a Comparator to compare two Lists

Comparator<List<Integer>> comparator = (list1, list2) -> {
    Iterator<Integer> iteratorA = list1.iterator();
    Iterator<Integer> iteratorB = list2.iterator();

    //It iterates through each list looking for an int that is not equal to determine which one precedes the other
    while (iteratorA.hasNext() && iteratorB.hasNext()) {
        int elementA = iteratorA.next();
        int elementB = iteratorB.next();

        if (elementA > elementB) {
            return 1;
        } else if (elementA < elementB) {
            return -1;
        }
    }
    //All elements seen so far are equal. Use the list size to decide
    return iteratorA.hasNext() ? 1 : iteratorB.hasNext() ? -1 : 0;
};

You can sort it as

Collections.sort(list, comparator);

EDIT: You can refer to David Geirola's answer to convert the version string as a POJO and move the comparator logic inside that. But that is highly tied/coupled to the input string format. My solution works for any List<List<Integer>>.

Comments

0

A simple object oriented approach would be to create object, representing version number, let's call it VersionNumber, which would have a constructor of a factory method that does the parsing of the string. This VersionNumber class should implement interface Comparable and implement method compareTo.

Here is a hint for using Comparable Why should a Java class implement comparable?

Then you can easily write an algorithm to find the max version or google some library that would do it for you.

Comments

0

It is not optimized but should work. You can use both of comparators.

static List<String> versions = Arrays.asList(
        "1_10_2_0_154",
        "3_10_5_2_10",
        "2_10_4_1_49",
        "3_10_5_1_37");

static Comparator<List<Integer>> c = (o1,o2) -> {
    int length = o1.size()>o2.size()?o2.size():o1.size();
    for (int i = 0; i < length; i++) {
        int i1 = o1.get(i);
        int i2 = o2.get(i);
        if (i1 != i2)
            return i1 - i2;
    }
    return 0;
};
static Comparator<List<Integer>> c2 = (o1,o2) -> {
    Iterator<Integer> i1=o1.iterator();
    Iterator<Integer> i2=o2.iterator();
    while (i1.hasNext() && i2.hasNext()){
        int i = i1.next()-i2.next();
        if (i!=0) return i;
    }
    return 0;
};

static Optional<List<Integer>> getTheMostRecentVersion(List<String> versions) {
    return versions.stream().
            map(s -> Arrays.stream(s.split("_")).
                    map(Integer::parseInt).
                    collect(Collectors.toList())).max(c2);
}

Comments

0

I think that this text file could be very big and it is better to compare each line on the fly (instead of store all line into collection to sort it after):

public static String getMostRecentVersion(BufferedReader in) throws IOException {
    final Comparator<String[]> version = (s1, s2) -> {
        int res = 0;

        for (int i = 0; i < 5 && res == 0; i++)
            res = Integer.compare(Integer.parseInt(s1[i]), Integer.parseInt(s2[i]));

        return res;
    };

    String str;
    String resStr = null;
    String[] resPparts = null;

    while ((str = in.readLine()) != null) {
        String[] parts = str.split("_");

        if (resStr == null || version.compare(parts, resPparts) > 0) {
            resStr = str;
            resPparts = parts;
        }
    }

    return resStr;
}

Comments

0

A general ListComparator should help.

static class ListComparator<T extends Comparable<T>> implements Comparator<List<T>> {

    @Override
    public int compare(List<T> o1, List<T> o2) {
        for (int i = 0; i < Math.max(o1.size(), o2.size()); i++) {
            int diff =
                    // Off the end of both - same.
                    i >= o1.size() && i >= o2.size() ? 0
                    // Off the end of 1 - the other is greater.
                    : i >= o1.size() ? -1
                    : i >= o2.size() ? 1
                    // Normal diff.
                    : o1.get(i).compareTo(o2.get(i));
            if (diff != 0) {
                return diff;
            }
        }
        return 0;
    }
}

private static final Comparator<List<Integer>> BY_VERSION = new ListComparator<Integer>().reversed();

public void test(String[] args) {
    String[] tests = {
            "1_10_2_0_154",
            "3_10_5_2_10",
            "2_10_4_1_49",
            "3_10_5_1_37",
            "3_10_5_1_37_0"
    };
    System.out.println("Before: " + Arrays.toString(tests));
    System.out.println("After:  " + Arrays.stream(tests)
            // Split into parts.
            .map(s -> s.split("_"))
            // Map String[] to List<Integer>
            .map(a -> Arrays.stream(a).map(s -> Integer.valueOf(s)).collect(Collectors.toList()))
            // Sort it.
            .sorted(BY_VERSION)
            // Back to a new list.
            .collect(Collectors.toList()));
}

Comments

-2

slap your arrays together into a number then just do number comparison.

class Scratch
{
    public static void main(String[] args)
    {
        List<List<Integer>> arr = new ArrayList<>();
        arr.add(fromArray(new Integer[]{1,10,2,0,154}));
        arr.add(fromArray(new Integer[]{3,10,5,2,10}));
        arr.add(fromArray(new Integer[]{2,10,4,1,49}));
        arr.add(fromArray(new Integer[]{3,10,5,1,37}));

        Integer[] maxLengths = {0,0,0,0,0};
        for (List<Integer> v : arr)
        {
            for(int idx = 0; idx < v.size(); idx++)
            {
                Integer n = v.get(idx);
                int curMaxLen = maxLengths[idx];
                maxLengths[idx] = Math.max(n.toString().length(), curMaxLen);
            }
        }
        Long largest = arr.stream().map(v -> {
            StringBuilder result = new StringBuilder();
            for(int idx = 0; idx < v.size(); idx++)
            {
                Integer n = v.get(idx);
                int maxLen = maxLengths[idx];
                result.append(String.format("%-" + maxLen + 's', n).replace(' ', '0'));
            }
            return result.toString();
        }).map(Long::valueOf).max(Comparator.naturalOrder()).get();
        System.out.println(largest);
    }

    public static List<Integer> fromArray(Integer[] array)
    {
        List<Integer> list = new ArrayList<>();
        Collections.addAll(list, array);
        return list;
    }
}

2 Comments

The question is in Java but your answer is in Groovy. In any case, this won't work because you need to use the same number of digits for each part of the version (adding leading zeros).
yeah im updating.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.