Search code examples
lambdajava-8guava

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.

Solution

  • 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.