Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conversion of List of CustomObject to Guava Table collection with less complexity

I have

class CustomObject {
Integer day;
List<InnerObject> innerObjects;
///getter setter

}

class InnerObject {
String id;
List<String> someVal;
//getter setter

}

I have a

List<CustomObject>

and I want

Table<String, Integer, List<String>>

I want table to represent id (from InnerObject) -> (day (from Custom object), List of someVal (from InnerObject)

Just to make it clean I tweaked names a bit but structure is same as what is expected.

Now how I am doing is

final List<CustomObject> objects = ???
final Map<Integer, List<InnerObject>> dayVsInnerObjects = objects.stream()
.collect(toMap(CustomObject::getDay, CustomObject::getInnerObjects));


final Table<String, Integer, List<String>> table = HashBasedTable.create();

 dayVsInnerObjects.forEach((day, innerObjects) -> 
                            innerObjects.forEach(i -> {
                             table.put(i.getId(), day, i.getSomeVal());
            })
);

My questions:

  1. Is there a better way of doing this? may be a better guava/Collection API that can make it a bit cleaner.
  2. Right now table is being populated and it is mutable. can we have a way to make it immutable while creating it.
  3. Time complexity if can be reduced here.
like image 257
Mohammad Adnan Avatar asked Jul 16 '17 15:07

Mohammad Adnan


2 Answers

You could use flatMap on the initial stream to get a stream of Map.Entry<Integer, InnerObject> (the key being the day) and use these entries to collect directly to a Table by means of Guava's Tables.toTable built-in collector:

Table<String, Integer, List<String>> table = objects.stream()
    .flatMap(custom -> custom.getInnerObjects().stream()
            .map(inner -> new SimpleEntry<>(custom.getDay(), inner)))
    .collect(Tables.toTable(
            entry -> entry.getValue().getId(),
            entry -> entry.getKey(),
            entry -> entry.getValue().getSomeVal(),
            HashBasedTable::create));

If you want the Table to be immutable, you can use Guava's method Tables.unmodifiableTable:

Table<String, Integer, List<String>> unmodifiableTable = Tables.unmodifiableTable(table);

Or, if you want to get the unmodifiable Table when collecting:

Table<String, Integer, List<String>> unmodifiableTable = objects.stream()
    .flatMap(custom -> custom.getInnerObjects().stream()
            .map(inner -> new SimpleEntry<>(custom.getDay(), inner)))
    .collect(Collectors.collectingAndThen(
            Tables.toTable(
                    entry -> entry.getValue().getId(),
                    entry -> entry.getKey(),
                    entry -> entry.getValue().getSomeVal(),
                    HashBasedTable::create),
            Tables::unmodifiableTable);

Note: I'm using Guava version 22.0, but as Eugene says in his comment below, this functionality is available since version 21.0.

As for your questions, I think I've already answered 1 and 2. Regarding 3, no, there's no way to reduce time complexity, as you need to access each InnerObject within each CustomObject.

like image 50
fps Avatar answered Nov 18 '22 00:11

fps


If I am not missing anything you can do a custom collector for that (I don't really understand why you are collecting first to a Map):

.stream()
.collect((Collector.of(
       HashBasedTable::create, 
       (table, co) -> {
           for (InnerObject io : co.getInnerObjects()) {
                table.put(io.getId(), co.getDay(), io.getSomeVal());
           }
       }, 
       (left, right) -> {
            left.putAll(right);
            return left;
       }));

EDIT as the other answer is already showing - there are build-in collectors for that already, since version 21.

like image 30
Eugene Avatar answered Nov 18 '22 01:11

Eugene