2

I have the following classes:

class Data {
        String systemId;
        String fileName;
        int x;
        int y;

        Data(String systemId, String fileName, int x, int y) {
            this.systemId = systemId;
            this.fileName = fileName;
            this.x = x;
            this.y = y;
        }
        public String getSystemId() {
            return systemId;
        }

        public void setSystemId(String systemId) {
            this.systemId = systemId;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }


class Result {
        int x;
        int y;

        Result(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }

List<Data> dataList = new ArrayList<>();
Data x1 = new Data("n1", "f1", 1, 2);
Data x2 = new Data("n1", "f1", 3, 4);
Data x3 = new Data("n1", "f1", 5, 6);
Data x4 = new Data("n1", "f2", 7, 8);
Data x5 = new Data("n2", "f1", 9, 10);
Data x6 = new Data("n2", "f2", 11, 12);
Data x7 = new Data("n3", "f1", 13, 14);
Data x8 = new Data("n4", "f1", 15, 16);

dataList.add(x1);dataList.add(x2);dataList.add(x3);dataList.add(x4);dataList.add(x5);dataList.add(x6);dataList.add(x7);dataList.add(x8);

I want to use Java streams to create a Map<String, List<Result>> out of the given input list. Also, the list values needs to be sorted in ascending order according to fields (x and y)

I need the output map to be as follows:

{"n1:f1" : [(1, 2), (3, 4), (5, 6)]
 "n1:f2" : [(7, 8)]
 "n2:f1" : [(9, 10)]
 "n2:f2" : [(11, 12)]
 "n3:f1" : [(13, 14)]
 "n4:f1" : [(15, 16)]
}

The key for the map is the combination of systemid and filename concatenated by colon. I tried grouping by the combination of systemid and filename but was not able to proceed with that approach.

Abhilash
  • 803
  • 1
  • 9
  • 32

1 Answers1

6

You can use groupingBy collector with mapping downstream as :

Map<String, List<Result>> map = data.stream()
        .collect(Collectors.groupingBy(a -> a.getSystemId() + ":" + a.getFileName()
                , Collectors.mapping(a -> new Result(a.getX(), a.getY()),
                        Collectors.toList())));
Naman
  • 27,789
  • 26
  • 218
  • 353
  • 2
    Note: This relies on the uniqueness of the grouping key `a -> a.getSystemId() + ":" + a.getFileName()` to produce correct results. – Naman Jun 24 '19 at 16:06
  • Yes, that's true. I used a similar approach using method references instead of a -> a.getSystemId() + ":" + a.getFileName() but it was giving compilation error. Can we achieved this with that? If not, why? – Abhilash Jun 24 '19 at 16:33
  • If by *achieving* you mean being able to use method reference for the creation of the grouping key. You can create util such as `String createGroupingKey(Data data)` which accepts a type of `Data` attribute to return the key in the expected format and then reference it as,`.groupingBy(UtilClass::createGroupingKey,....` – Naman Jun 24 '19 at 17:59
  • No, what I meant was why can't we have something like this for key creation Data::getSystemId + ":" + Data::getFileName – Abhilash Jun 25 '19 at 04:36
  • 2
    @Abhilash28Abhi that's just not how method references work. A method reference stands for a `Function` object in this case, not the potential result of the method call. If you cast each method reference, you can concatenate them with a string, but that would still not do what you'd want (try this for example: `((Function) String::format) + ":" + ((Function) String::trim)`). Writing it as a lambda expression is the way to go. – Malte Hartwig Jun 25 '19 at 06:22
  • 1
    @Abhilash28Abhi Simple answer to that could be `Data::getSystemId` is not a `String` if you are considering it to be. – Naman Jun 25 '19 at 06:39
  • @Naman I also wanted to sort the values in ascending order for fields (x and y) and then store it in the list. How can I achieve that? – Abhilash Jun 26 '19 at 10:17
  • @Abhilash28Abhi I sense I had missed this comment, to sort the list of values you can use `collectingAndThen` as in [this answer](https://stackoverflow.com/questions/35872236/sorting-lists-after-groupingby). Related to subsequent questions: It's always better to ask a new question(if not already present on SO) rather than comments or edits to the existing question. – Naman Jun 27 '19 at 06:19
  • @Naman I wanted to avoid duplicating this question again so thought of asking in comments. The only question I had was if I can use collectingAndThen while creating the map itself or I will have to sort the list separately from the map which is created? – Abhilash Jun 27 '19 at 07:11