Partition a Stream by a discriminator function - java

One of the missing features in the Streams API is the "partition by" transformation, for example as defined in Clojure. Say I want to reproduce Hibernate's fetch join: I want to issue a single SQL SELECT statement to receive this kind of objects from the result:
class Family {
String surname;
List<String> members;
}
I issue:
SELECT f.name, m.name
FROM Family f JOIN Member m on m.family_id = f.id
ORDER BY f.name
and I retrieve a flat stream of (f.name, m.name) records. Now I need to transform it into a stream of Family objects, with a list of its members inside. Assume I already have a Stream<ResultRow>; now I need to transform it into a Stream<List<ResultRow>> and then act upon that with a mapping transformation which turns it into a Stream<Family>.
The semantics of the transformation are as follows: keep collecting the stream into a List for as long as the provided discriminator function keeps returning the same value; as soon as the value changes, emit the List as an element of the output stream and start collecting a new List.
I hope to be able to write this kind of code (I already have the resultStream method):
Stream<ResultRow> dbStream = resultStream(queryBuilder.createQuery(
"SELECT f.name, m.name"
+ " FROM Family f JOIN Member m on m.family_id = f.id"
+ " ORDER BY f.name"));
Stream<List<ResultRow> partitioned = partitionBy(r -> r.string(0), dbStream);
Stream<Family> = partitioned.map(rs -> {
Family f = new Family(rs.get(0).string(0));
f.members = rs.stream().map(r -> r.string(1)).collect(toList());
return f;
});
Needless to say, I expect the resulting stream to stay lazy (non-materialized) as I want to be able to process a result set of any size without hitting any O(n) memory limits. Without this crucial requirement I would be happy with the provided groupingBy collector.

The solution requires us to define a custom Spliterator which can be used to construct the partitioned stream. We shall need to access the input stream through its own spliterator and wrap it into ours. The output stream is then constructed from our custom spliterator.
The following Spliterator will turn any Stream<E> into a Stream<List<E>> provided a Function<E, ?> as the discriminator function. Note that the input stream must be ordered for this operation to make sense.
import java.util.*;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Comparator.naturalOrder;
public class PartitionBySpliterator<E> extends AbstractSpliterator<List<E>> {
private final Spliterator<E> spliterator;
private final Function<? super E, ?> partitionBy;
private HoldingConsumer<E> holder;
private Comparator<List<E>> comparator;
public PartitionBySpliterator(
Spliterator<E> toWrap,
Function<? super E, ?> partitionBy
) {
super(Long.MAX_VALUE, toWrap.characteristics() & ~SIZED | NONNULL);
this.spliterator = toWrap;
this.partitionBy = partitionBy;
}
public static <E> Stream<List<E>> partitionBy(
Function<E, ?> partitionBy, Stream<E> in
) {
return StreamSupport.stream(
new PartitionBySpliterator<>(in.spliterator(), partitionBy), false);
}
#Override
public boolean tryAdvance(Consumer<? super List<E>> action) {
final HoldingConsumer<E> h;
if (holder == null) {
h = new HoldingConsumer<>();
if (!spliterator.tryAdvance(h)) {
return false;
}
holder = h;
} else {
h = holder;
}
final ArrayList<E> partition = new ArrayList<>();
final Object partitionKey = partitionBy.apply(h.value);
boolean didAdvance;
do {
partition.add(h.value);
}
while ((didAdvance = spliterator.tryAdvance(h))
&& Objects.equals(partitionBy.apply(h.value), partitionKey));
if (!didAdvance) {
holder = null;
}
action.accept(partition);
return true;
}
static final class HoldingConsumer<T> implements Consumer<T> {
T value;
#Override
public void accept(T value) {
this.value = value;
}
}
#Override
public Comparator<? super List<E>> getComparator() {
final Comparator<List<E>> c = this.comparator;
return c != null ? c : (this.comparator = comparator());
}
private Comparator<List<E>> comparator() {
#SuppressWarnings({"unchecked", "rawtypes"})
final Comparator<? super E> innerComparator =
Optional.ofNullable(spliterator.getComparator())
.orElse((Comparator) naturalOrder());
return (left, right) -> {
final int c = innerComparator.compare(left.get(0), right.get(0));
return c != 0 ? c : innerComparator.compare(
left.get(left.size() - 1), right.get(right.size() - 1));
};
}
}

For those of you who just want to partition a stream, there are mappers and collectors for that.
class Person {
String surname;
String forename;
public Person(String surname, String forename) {
this.surname = surname;
this.forename = forename;
}
#Override
public String toString() {
return forename;
}
}
class Family {
String surname;
List<Person> members;
public Family(String surname, List<Person> members) {
this.surname = surname;
this.members = members;
}
#Override
public String toString() {
return "Family{" + "surname=" + surname + ", members=" + members + '}';
}
}
private void test() {
String[][] data = {
{"Kray", "Ronald"},
{"Kray", "Reginald"},
{"Dors", "Diana"},};
// Their families.
Stream<Family> families = Arrays.stream(data)
// Build people
.map(a -> new Person(a[0], a[1]))
// Collect into a Map<String,List<Person>> as families
.collect(Collectors.groupingBy(p -> p.surname))
// Convert them to families.
.entrySet().stream()
.map(p -> new Family(p.getKey(), p.getValue()));
families.forEach(f -> System.out.println(f));
}

It can be done by collapse with StreamEx
StreamEx.of(queryBuilder.createQuery(
"SELECT f.name, m.name"
+ " FROM Family f JOIN Member m on m.family_id = f.id"
+ " ORDER BY f.name"))
.collapse((a, b) -> a.string(0).equals(b.string(0)), Collectors.toList())
.map(l -> new Family(l.get(0).string(0), StreamEx.of(l).map(r -> r.string(1)).toList()))
.forEach(System.out::println);

Related

Dynamic datatype in generics java

I have a usecase where client is sending a List<Function>. Task is to iterate and execute this function and keep it in a TypedSafeMap.
pseudo client code:
Function<String, Integer> firstFn = x -> x.length();
Function<String, String> secondFn = x -> x.substring(0);
client.runTheseFunctions(Arrays.asList(firstFn, secondFn));
Inside runtTheseFunctions in the code, task is to execute these functions and keep it in a TypedSafeMap where the key is the datatype of the type of the result of the function and value is the return of functions.apply();
The code below
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
lst.stream().forEach( x -> {
typedSafeMap.put(????, x.apply(o));
//The key is nothing but the datatype of the x.apply(o).
//How do I add this in runtime here. Generics is all compile time safety.
});
}
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
lst.stream().collect(Collectors.toMap(f -> f.apply(o).getClass(), f -> f.apply(o)));
}
You can implement your "runTheseFunctions" method as shown below:
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
Map<Class<?>, Object> typedSafeMap = new HashMap<>();
lst.stream().forEach(x -> {
Object value = x.apply(o);
typedSafeMap.put(value.getClass(), value);
});
System.out.println(typedSafeMap);
}
In case the List of Functions contains two or more Functions with the same outputtype (for instance: String getFirstName, String getLastName, toMap will fail. So an alternative is:
var map = list.stream().collect(groupingBy(
f -> f.apply(e).getClass(),
mapping(f -> f.apply(e), toList())
));
Here is an example of what you want to achieve, and you can use for your tests. I assumed an trivial implementation of Employee class, just to give you an idea:
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
class Employee {
String name;
public Employee(String name) {
this.name = name;
}
public int length() {
return name.length();
}
public String substring(int index) {
return name.substring(index);
}
}
public class Test {
public static void main(String[] args) {
Employee e = new Employee("Marco");
Function<Employee, Integer> firstFn = x -> x.length();
Function<Employee, String> secondFn = x -> x.substring(0);
runTheseFunctions(Arrays.asList(firstFn, secondFn), e);
}
public static void runTheseFunctions(List<Function<Employee, ?>> lst, Employee o) {
Map<Class, Object> typedSafeMap = new HashMap<>();
lst.stream().forEach(x -> {
Object result = x.apply(o);
typedSafeMap.put(x.apply(o).getClass(), x.apply(o));
// The key is nothing but the datatype of the x.apply(o).
// How do I add this in runtime here. Generics is all compile time safety.
});
typedSafeMap.entrySet().forEach(entry -> System.out.println(entry.getKey() + " - " + entry.getValue()));
}
}
And here is the output:
class java.lang.String - Marco
class java.lang.Integer - 5
Enhancing #Yonas answer:
private static Map<?, ? extends Object> runTheseFunctions(List<Function<String, ? extends Object>> list, String o) {
return list.stream()
.map(f -> f.apply(o))
.collect(Collectors.toMap(result -> result.getClass(), Function.identity()));
}
This will call the f.apply(o) only once.

Optimize number for SQL queries

I use this code to to generate chart with data:
#GetMapping("/terminals")
public ResponseEntity<Map<String, List<TopTerminalsDTO>>> getTopTerminals(
#RequestParam(value = "start_date", required = true) String start_date,
#RequestParam(value = "end_date", required = true) String end_date) {
final List<PaymentTransactionsDailyFacts> list = dashboardService.findTop_Terminals(start_dateTime, end_dateTime);
final Collector<PaymentTransactionsDailyFacts, List<TopTerminalsDTO>, List<TopTerminalsDTO>> terminalsCollector = Collector
.of(ArrayList::new, (terminals, p) -> terminals.add(mapper.toTopTerminalsDTO(p)),
(accumulator, terminals) -> {
accumulator.addAll(terminals);
return accumulator;
});
final Map<String, List<TopTerminalsDTO>> final_map = list.stream().filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(p -> getTerminalName(p.getTerminal_id()), terminalsCollector));
return ResponseEntity.ok(final_map);
}
private String getTerminalName(Integer id) {
Optional<Terminals> obj = terminalService.findById(id);
return obj.map(Terminals::getName).orElse("");
}
But I noticed that getTerminalName is called more than 10 times to translate the name from number. Do you know how I can reduce the number of calls with some optimization?
Modify findTop_Terminals and PaymentTransactionsDailyFacts to include the name (using a SQL LEFT JOIN clause).
Alternative, scan the list for all terminal ids, then call a List<Terminals> list = terminalService.findByIds(idList); method to get all those terminals using a SQL IN clause.
Note: Beware limit on how many ? markers can be in a SQL statement.
Then build a Map<Integer, String> mapping terminal id to name, and replace getTerminalName method with map lookup.
Sounds like a case for a temporary cache, scoped to just this request, or perhaps longer if the terminal names are stable enough.
Clearly something like ehCache behind the scenes would suit this well, but I am often tempted with a tactical bit of caching, especially if I don't want to keep the cached values beyond this immediate request.
For example:
TerminalNameCache cache = new TerminalNameCache();
final Map<String, List<TopTerminalsDTO>> final_map = list.stream()
.filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(
p -> cache.getTerminalName(p.getTerminal_id()),
terminalsCollector));
Then the TerminalNameCache is just an inner class in the parent Controller class. This is to allow it to call the existing private String getTerminalName(Integer id) method from the question (shown as on ParentControllerClass below):
private class TerminalNameCache {
private final Map<Integer, String> cache = new ConcurrentHashMap<>();
private String getTerminalName(Integer id) {
return cache.computeIfAbsent(id,
id2 -> ParentControllerClass.this.getTerminalName(id2));
}
}
If this looks like emerging into a pattern, it would be worth refactoring the caching to something more re-useable. E.g. this could based around caching calls to a generic function:
public class CachedFunction<T, R> implements Function<T, R> {
private final Function<T, R> function;
private final Map<T, R> cache = new ConcurrentHashMap<>();
public CachedFunction(Function<T, R> function) {
this.function = function;
}
#Override
public R apply(T t) {
return cache.computeIfAbsent(t, t2 -> function.apply(t2));
}
}
This would then be used like this:
CachedFunction<Integer, String> cachedFunction = new CachedFunction<>(
id -> getTerminalName(id));
final Map<String, List<TopTerminalsDTO>> final_map = list.stream()
.filter(p -> p.getTerminal_id() != null)
.collect(Collectors.groupingBy(
p -> cachedFunction.apply(p.getTerminal_id()),
terminalsCollector));

Get parent object from stream

I have this class:
public class StructUserType extends UserType {
MembersList membersList = new MembersList();
public List<Member> getMembers() {
return Collections.unmodifiableList(membersList.members);
}
static class MembersList {
List<Member> members = new ArrayList<>();
}
public static class Member implements Identifiable {
private Integer id;
public Integer getId() {
return id;
}
}
}
And I have a List object:
List<SmbpUserType> userTypes = new ArrayList<>();
I want find Member which is equal to a certain id. I tried as follows:
Integer id = 1;
userTypes.stream()
.filter(StructUserType.class::isInstance)
.map(StructUserType.class::cast)
.forEach(structUserType -> {
structUserType.getMembers()
.stream()
.filter(m -> m.getId() == id)
.findFirst().orElse(null);
});
I want, when the filter in the internal stream runs and finds the first member, to return the parent element that this member contains, those UserType.
Analog in the classical style:
for (UserType userType : userTypes) {
if (userType instanceof StructUserType) {
List<StructUserType.Member> members = ((StructUserType) userType).getMembers();
for (StructUserType.Member member : members) {
if (member.getId() == id) {
return userType;
}
}
}
}
return null;
Replace forEach with filter, to find StructUserType instances that satisfy the condition of the inner stream pipeline. Then get the first element of the Stream, if such exists.
return
userTypes.stream()
.filter(StructUserType.class::isInstance)
.map(StructUserType.class::cast)
.filter(st -> st.getMembers()
.stream()
.anyMatch(m -> m.getId().equals(id)))
.findFirst()
.orElse(null);
Instead of forEach you can use a filter for the nested Stream.
At last you can return the first match or collect all matches
...
.filter(structUser -> structUser.getMembers()
.stream()
.anyMatch(member -> member.getId().equals(id))
)
...

Generic method of mapping variables from 1 class to another

Coming from C++ and currently employed in a Java environment, I was wondering how I would be able to create a mapping of void* and void* in Java in order to create a generic mapping from A to B and from B to A. I am aware that Java doesn't have pointers and references the way C++ does, but am failing to find a method that would still allow this.
An example of what I am trying to achieve:
public class A{
#GenericMapping(1)
private Integer temp1;
}
public class B{
#GenericMapping(1)
private Integer temp2;
}
public class Mapper{
private List<Pair<Integer, Integer>> mapping;
public void map(Object ObjectOfAnyClassButLetsAssumeA, Object ObjectOfAnyClassButLetsAssumeB){
// Get all parameters with GenericMapping above it, get its value
// and match the corresponding value with the value of B
// Resulting in A.temp1 = B.temp2;
}
}
However, if possible I'd rather create a map (like map[A.temp1] = B.temp2) in order to avoid using the #GenericMapping, seeing as that would allow me to not modify the class in any way and still facilitate its mapping.
I think I understand what you want to do here and you can accomplish it with some metadata and Java 8's Lambdas.
What we do is set up a helper class that contains all mappings identified by class and IDs (analogous to your #GenericMapping but without actually annotating the classes) and containing methods for setting and getting the value. It's important that all mappings for the same ID have the same value type or a ClassCastException may be thrown when transferring values.
My example uses three classes where not all mappings apply to all classes.
Here's the code:
public class GenericMappingDemo {
static class A {
private Integer integerA;
private String stringA;
private Float floatA;
public A(final Integer integerA, final String stringA, final Float floatA) {
this.integerA = integerA;
this.stringA = stringA;
this.floatA = floatA;
}
public Integer getIntegerA() {
return integerA;
}
public void setIntegerA(final Integer integerA) {
this.integerA = integerA;
}
public String getStringA() {
return stringA;
}
public void setStringA(final String stringA) {
this.stringA = stringA;
}
public Float getFloatA() {
return floatA;
}
public void setFloatA(final Float floatA) {
this.floatA = floatA;
}
#Override
public String toString() {
return "A{integerA=" + integerA + ", stringA='" + stringA + "', floatA=" + floatA + '}';
}
}
static class B {
private Integer integerB;
private String stringB;
public Integer getIntegerB() {
return integerB;
}
public void setIntegerB(final Integer integerB) {
this.integerB = integerB;
}
public String getStringB() {
return stringB;
}
public void setStringB(final String stringB) {
this.stringB = stringB;
}
#Override
public String toString() {
return "B{integerB=" + integerB + ", stringB='" + stringB + '\'' + '}';
}
}
static class C {
private Float floatC;
private String stringC;
public Float getFloatC() {
return floatC;
}
public void setFloatC(final Float floatC) {
this.floatC = floatC;
}
public String getStringC() {
return stringC;
}
public void setStringC(final String stringC) {
this.stringC = stringC;
}
#Override
public String toString() {
return "C{floatC=" + floatC + ", stringC='" + stringC + "'}";
}
}
static class GenericMapping<C, T> {
final int id;
final Class<C> type;
final Function<C, T> getter;
final BiConsumer<C, T> setter;
public GenericMapping(final int id,
final Class<C> type,
final Function<C, T> getter,
final BiConsumer<C, T> setter) {
this.id = id;
this.type = type;
this.getter = getter;
this.setter = setter;
}
}
static class Mapper {
// All mappings by class and id
private final Map<Class<?>, Map<Integer, GenericMapping<?, ?>>> mappings
= new HashMap<>();
public void addMapping(GenericMapping<?, ?> mapping) {
mappings.computeIfAbsent(mapping.type,
c -> new TreeMap<>()).put(mapping.id, mapping);
}
/**
* Map values from one object to another,
* using any mapping ids that apply to both classes
* #param from The object to transfer values from
* #param to The object to transfer values to
*/
public <From, To> void map(From from, To to) {
Map<Integer, GenericMapping<?, ?>> getters
= mappings.get(from.getClass());
Map<Integer, GenericMapping<?, ?>> setters
= mappings.get(to.getClass());
if (getters == null || setters == null) {
// Nothing to do
return;
}
// Create a set with the ids in both getters and
// setters, i.e. the mappings that apply
Set<Integer> ids = new HashSet<>(getters.keySet());
ids.retainAll(setters.keySet());
// Transfer all mappings
for (Integer id : ids) {
GenericMapping<From, ?> getter
= (GenericMapping<From, ?>) getters.get(id);
GenericMapping<To, ?> setter
= (GenericMapping<To, ?>) setters.get(id);
transfer(from, to, getter, setter);
}
}
private <From, To, V> void transfer(final From from,
final To to, final GenericMapping<From, ?> getter,
final GenericMapping<To, V> setter) {
// This will throw an exception if the mappings are invalid
final V value = (V) getter.getter.apply(from);
setter.setter.accept(to, value);
}
}
public static void main(String[] args) {
final Mapper mapper = new Mapper();
// Mapping definition for class A
mapper.addMapping(new GenericMapping<>(1, A.class,
A::getIntegerA, A::setIntegerA));
mapper.addMapping(new GenericMapping<>(2, A.class,
A::getStringA, A::setStringA));
mapper.addMapping(new GenericMapping<>(3, A.class,
A::getFloatA, A::setFloatA));
// Mapping definition for class B
mapper.addMapping(new GenericMapping<>(1, B.class,
B::getIntegerB, B::setIntegerB));
mapper.addMapping(new GenericMapping<>(2, B.class,
B::getStringB, B::setStringB));
// Mapping definition for class C
mapper.addMapping(new GenericMapping<>(2, C.class,
C::getStringC, C::setStringC));
mapper.addMapping(new GenericMapping<>(3, C.class,
C::getFloatC, C::setFloatC));
// Use the mappings
A a = new A(7, "foo", 3.7f);
B b = new B();
C c = new C();
System.out.printf("A before map: %s%n", a);
System.out.printf("B before map: %s%n", b);
System.out.printf("C before map: %s%n", c);
// This will transfer a.integerA to b.integerB and a.stringA to b.stringB
mapper.map(a, b);
// This will transfer a.stringA to c.stringC and a.floatA to c.floatC
mapper.map(a, c);
System.out.println();
System.out.printf("A after map: %s%n", a);
System.out.printf("B after map: %s%n", b);
System.out.printf("C after map: %s%n", c);
}
}
And the result after running it:
A before map: A{integerA=7, stringA='foo', floatA=3.7}
B before map: B{integerB=null, stringB='null'}
C before map: C{floatC=null, stringC='null'}
A after map: A{integerA=7, stringA='foo', floatA=3.7}
B after map: B{integerB=7, stringB='foo'}
C after map: C{floatC=3.7, stringC='foo'}
Java 7
The same general solution can be used for Java 7, but it will be a lot more verbose. Since Java 7 doesn't have the functional interfaces Function<U, V> and BiConsumer<U, V> you'll need to define these yourself, which isn't that much trouble. It could be argued that they should be defined in Java 8 too so interface and method names makes more sense (e.g. Getter.get and Setter.set).
The big thing is the mapping definitions which will have to use anonymous classes instead of lambdas - lambdas is mostly syntactic sugar for anonymous classes with only one method anyways, but they make the code a lot more readable.
The mapping for a.integerA will look like this in Java 7:
mapper.addMapping(new GenericMapping<>(1, A.class,
new Function<A, Integer>() {
#Override
public Integer apply(final A a1) {
return a1.getIntegerA();
}
},
new BiConsumer<A, Integer>() {
#Override
public void accept(final A a1, final Integer integerA) {
a1.setIntegerA(integerA);
}
}));
You could also have a look at Apache Commons BeanUtils, which also have a quite sophisticated, although explicit (not Annotation-based), Converter API:
http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.3/apidocs/org/apache/commons/beanutils/Converter.html
http://commons.apache.org/proper/commons-beanutils/javadocs/v1.9.3/apidocs/org/apache/commons/beanutils/ConvertUtilsBean.html

Java: how to transform from List<T> to Map<f1(T), List(f2(T))> without iterating

I have a list of objects that I need to transform to a map where the keys are a function of each element, and the values are lists of another function of each element. Effectively this is grouping the elements by a function of them.
For example, suppose a simple element class:
class Element {
int f1() { ... }
String f2() { ... }
}
and a list of these:
[
{ f1=100, f2="Alice" },
{ f1=200, f2="Bob" },
{ f1=100, f2="Charles" },
{ f1=300, f2="Dave" }
]
then I would like a map as follows:
{
{key=100, value=[ "Alice", "Charles" ]},
{key=200, value=[ "Bob" ]},
{key=300, value=[ "Dave" ]}
}
Can anyone suggest a succinct way of doing this in Java without iterating? A combination of LambdaJ's group method with Guava's Maps.transform nearly gets there, but group doesn't generate a map.
Guava has Maps.uniqueIndex(Iterable values, Function keyFunction) and Multimaps.index(Iterable values, Function keyFunction), but they don't transform the values. There are some requests to add utility methods that do what you want, but for now, you'll have to roll it yourself using Multimaps.index() and Multimaps.transformValues():
static class Person {
private final Integer age;
private final String name;
public Person(Integer age, String name) {
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public String getName() {
return name;
}
}
private enum GetAgeFunction implements Function<Person, Integer> {
INSTANCE;
#Override
public Integer apply(Person person) {
return person.getAge();
}
}
private enum GetNameFunction implements Function<Person, String> {
INSTANCE;
#Override
public String apply(Person person) {
return person.getName();
}
}
public void example() {
List<Person> persons = ImmutableList.of(
new Person(100, "Alice"),
new Person(200, "Bob"),
new Person(100, "Charles"),
new Person(300, "Dave")
);
ListMultimap<Integer, String> ageToNames = getAgeToNamesMultimap(persons);
System.out.println(ageToNames);
// prints {100=[Alice, Charles], 200=[Bob], 300=[Dave]}
}
private ListMultimap<Integer, String> getAgeToNamesMultimap(List<Person> persons) {
ImmutableListMultimap<Integer, Person> ageToPersons = Multimaps.index(persons, GetAgeFunction.INSTANCE);
ListMultimap<Integer, String> ageToNames = Multimaps.transformValues(ageToPersons, GetNameFunction.INSTANCE);
// Multimaps.transformValues() returns a *lazily* transformed view of "ageToPersons"
// If we want to iterate multiple times over it, it's better to create a copy
return ImmutableListMultimap.copyOf(ageToNames);
}
A re-usable utility method could be:
public static <E, K, V> ImmutableListMultimap<K, V> keyToValuesMultimap(Iterable<E> elements, Function<E, K> keyFunction, Function<E, V> valueFunction) {
ImmutableListMultimap<K, E> keysToElements = Multimaps.index(elements, keyFunction);
ListMultimap<K, V> keysToValuesLazy = Multimaps.transformValues(keysToElements, valueFunction);
return ImmutableListMultimap.copyOf(keysToValuesLazy);
}
I guess we could improve the generics in the signature by using Function<? extends E, K> or something, but I don't have the time to delve further...
Now with Java8 you can do it like:
static class Element {
final int f1;
final String f2;
Element(int f1, String f2) {
this.f1 = f1;
this.f2 = f2;
}
int f1() { return f1;}
String f2() { return f2; }
}
public static void main(String[] args) {
List<Element> elements = new ArrayList<>();
elements.add(new Element(100, "Alice"));
elements.add(new Element(200, "Bob"));
elements.add(new Element(100, "Charles"));
elements.add(new Element(300, "Dave"));
elements.stream()
.collect(Collectors.groupingBy(
Element::f1,
Collectors.mapping(Element::f2, Collectors.toList())
))
.forEach((f1, f2) -> System.out.println("{"+f1.toString() + ", value="+f2+"}"));
}
There has been some discussion in adding one API in Apache's CollectionUtils to transform a List to Map, but then I dont see any reason for not using a foreach contruct, Is there any problem that you are facing ? Transform will do the same thing which you can get easily by foreach, looping cannot be avoided.
EDIT:
Here is the link to discussion in Apache's forum http://apache-commons.680414.n4.nabble.com/Convert-List-to-Map-td747218.html
I don't know why you don't want to iterate. JDK does not support transform, but you can implement it yourself.
If you are worried about the performance, even if JDK had supported it, it would have also iterated it.

Categories

Resources