1

I have a list of Date/time, CPM numbers separated by commas. I have to read through the file and put it in an ArrayList sorted by the max CPM numbers and only keeping the lines containing the top 5 CPMs.

public class Geiger implements Comparable<Geiger> {
    private String date;
    private int CPM;

    public static void main(String[] args) {
        List<String> dGeigers = new ArrayList<>();
        try {
            BufferedReader br = new BufferedReader(new FileReader("test.txt"));
            String nextLine;
            while ((nextLine = br.readLine()) != null) {
                if (nextLine.contains("Every Minute")) {
                    String[] values = nextLine.split(",");
                    Collections.addAll(dGeigers, values[0] + "\t" + values[2] + "\n");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Collections.sort(dGeigers);
        System.out.println(dGeigers.toString());
    }

    public Geiger(String date, String cPM2) {
        this.date = date;
        this.CPM = Integer.valueOf(cPM2);
    }

    public String getDate() {
        return date;
    }

    public int getCPM() {
        return CPM;
    }

    public String toString() {
        return date + "\t" + CPM;
    }

    @Override
    public int compareTo(Geiger cpmSort) {
        if (this.CPM < cpmSort.CPM) {
            return -1;
        } else if (this.CPM == cpmSort.CPM) {
            return 0;
        } else {
            return 1;
        }
    }
}

Here is the code I have so far. It manages to sort by the first column, which would be the Date/time, but I can't figure out how to sort it by the CPM column.

This
is
a
11111, Every Minute, 14
00231, Every Minute, 24
00011, Every Minute, 30
00201, Every Minute, 25
00201, Every Minute, 26
00301, Every Minute, 7
00401, Every Minute, 566
02301, Every Minute, 3230
02301, Every Minute, 3540
00231, Every Minute, 214
test file 
sdfasdf asdfadg sdfh sghsgfh sfgh sfdg sdgh

here is the test file that I used.

Summawata
  • 13
  • 3
  • Add also part of example input file. – Boken Mar 10 '21 at 22:14
  • By the implementation of method `compareTo` the list is sorted by CPM field, while field `date` is not involved in ordering. – Nowhere Man Mar 10 '21 at 22:18
  • You should use Integer.compare(int, int) in the compareTo method rather than manually writing out the branches. – bliss Mar 10 '21 at 22:20
  • @bliss could you elaborate for me? what is the difference between using Integer.compare(int,int) and the one I used? – Summawata Mar 10 '21 at 22:25
  • @Summawata Take a look at the [relevant documentation](https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#compare(int,%20int)). It does the same comparison you've written out in a single line of code: "return Integer.compare(this.CPM, cpmSort.CPM)". It's available to other primitive classes as well. – bliss Mar 10 '21 at 22:51

4 Answers4

1

It looks like you want to to create a list of Geiger and sort on the CPM part.

Here is what I recommend.

  • define a comparator.
  • read in the lines.
  • create a new instance of Geiger based on the split arguments.
  • sort them in reverse order and save the top five
Comparator<Geiger> comp = Comparator.comparing(Geiger::getCPM,
        Comparator.reverseOrder());
try {
    List<Geiger> dGeigers = Files
            .lines(Path.of("f:/GeigerTestData.txt"))
            .filter(line -> line.contains("Every Minute"))
            .map(line -> line.split(","))
            .map(v -> new Geiger(v[0], v[2])).sorted(comp)
            .limit(5).collect(Collectors.toList());
    
dGeigers.forEach(System.out::println);
    
} catch (Exception e) {
    e.printStackTrace();
}

Using your supplied data, here is what the above prints.

02301   3540
02301   3230
00401   566
00231   214
00011   30

I received an exception because you have spaces in your fields and Integer.parseInt() complains. I suggest you change your method to the following to trim the white space.

    public Geiger(String date, String cPM2) {
        this.date = date;
        this.CPM = Integer.valueOf(cPM2.trim());
    }

I also recommend you make Geiger its own class without the static main entry point. Normally, you have a driver class that reads in the data and uses other classes and mechanisms to manipulate the data.

WJS
  • 36,363
  • 4
  • 24
  • 39
0

You coded the compare method directly inside your class - when you sort these comparables the sort order is hardcoded.

For more flexible sorting, extract the comparison into a Comparator, then use that for sorting. See How to use Comparator in Java to sort

Queeg
  • 7,748
  • 1
  • 16
  • 42
0

Sorting - one field

When you are comparing two int or Integer you can use your idea:

if (this.cpm < cpmSort.cpm) {
    return -1;
} else if (this.cpm == cpmSort.cpm) {
    return 0;
} else {
    return 1;
}

use method from Integer class:

return Integer.compare(cpm, cpmSort.cpm);

or just substract:

return cpm - cpmSort.cpm;

Sorting - two fields

Sorting first by cpm next by date:

@Override
public int compareTo(Geiger cpmSort) {
    int byCpm = Integer.compare(cpm, cpmSort.cpm);

    if (byCpm == 0) {
        // "cpm" values are equals, so you have to use different field
        return date.compareTo(cpmSort.date);
    } else {
        // "cpm" vales are NOT equal
        return byCpm;
    }
}

Sorting first by date next by cpm:

@Override
public int compareTo(Geiger cpmSort) {
    int byDate = date.compareTo(cpmSort.date);

    if (byDate == 0) {
        // "date" values are equals, so you have to use different field
        return Integer.compare(cpm, cpmSort.cpm);
    } else {
        // "date" vales are NOT equal
        return byDate;
    }
}

Class clean up

Remember to split model class (Geiger) from "main" (runnable) class. For example:

Main.java:

public class Main {

    public static void main(String[] args) {
        List<Geiger> geigers = readFromFile();

        Collections.sort(geigers);

        System.out.println(geigers.toString());
    }

    private static List<Geiger> readFromFile() {
        List<Geiger> geigers = new ArrayList<>();

        try {
            BufferedReader br = new BufferedReader(new FileReader("test.txt"));
            String nextLine;
            while ((nextLine = br.readLine()) != null) {
                if (nextLine.contains("Every Minute")) {
                    String[] values = nextLine.split(", ");

                    if (values.length == 3) {
                        String date = values[0];
                        String cpm = values[2];

                        Geiger geiger = new Geiger(date, cpm);

                        geigers.add(geiger)
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return geigers;
    }
}

and Geiger.java

public class Geiger implements Comparable<Geiger> {
    private String date;
    private int cpm;

    public Geiger(String date, String cpm) {
        this.date = date;
        this.cpm = Integer.parseInt(cpm);
    }

    public String getDate() {
        return date;
    }

    public int getCpm() {
        return cpm;
    }

    public String toString() {
        return date + "\t" + cpm;
    }

    @Override
    public int compareTo(Geiger cpmSort) {
        if (this.cpm < cpmSort.cpm) {
            return -1;
        } else if (this.cpm == cpmSort.cpm) {
            return 0;
        } else {
            return 1;
        }
    }
}
Boken
  • 4,825
  • 10
  • 32
  • 42
  • Thank you so much, I'm gonna try and understand how I messed up in my code. – Summawata Mar 10 '21 at 22:52
  • @Summawata first of all - split files in to two (model and main). You can just copy-paste. After that you can adjust your `compareTo` method. Don't know what you need. – Boken Mar 10 '21 at 22:54
0
  1. In existing code, method compareTo of Geiger class is not used at all, because no instance of Geiger class is constructed, which should be fixed as follows:
List<Geiger> dGeigers = new ArrayList<>();
// ...

if (nextLine.contains("Every Minute")) {
    String[] values = nextLine.split(",");
    dGeigers.add(new Geiger(values[0], values[2]));
}
  1. If comparison by CPM fields is needed in reverse order (keep the max), method compareTo should look like:
@Override
public int compareTo(Geiger cpmSort) {
    return Integer.compare(cpmSort.CPM, this.CPM); // change argument order to sort in descending order
}
  1. If top 5 elements need to be selected from the dGeigers list, there are several ways:
  • Use List::subList
Collections.sort(dGeigers); // will sort using Geiger::compareTo
List<Geiger> top5 = dGeigers.subList(0, Math.min(5, dGeigers.size()));
  • Use Stream API:
List<Geiger> top5 = dGeigers.stream()
    .sorted() // using updated `compareTo` method
    .limit(5)
    .collect(Collectors.toList());
Nowhere Man
  • 19,170
  • 9
  • 17
  • 42