0

Let's say I have a list of Foo objects. There is a property on Foo that I must use to fetch Bar objects from a data source. I must then map each bar object back to the original Foo object I got Bar for.

public class Foo {
   int barId;
}

public class Bar {
   int barId;
}

Set<Foo> inputFoo;
Map<Bar, Foo> barToFoo;

public Bar getBar(int barId);

My attempt is as follows:

List<Bar> allBarsInFoo = inputFoos.stream()
.map(Foo::barId)
.forEach(b -> getBar(b))

I do not know how to get the map of Bar to Foo without doing more heavy and unnecessary ops after the above.

Edit: To make my question more general, is there a way to keep a reference to the original object, perform a bunch of filter/map operations in a stream off the list of original objects, and then map the result of the filter/map as the key and the original object as the value of the resulting map?

1
  • Keep in mind you talk about heavy and unnecessary computations. In Java, the number of computations are not tightly correlated with the amount of typing. Do not think that a shorter to write solution is more performant, instead benchmark. With all the opportunities Java has to optimize, only bench marking will really tell you which approach is faster. Commented Nov 4, 2016 at 8:16

2 Answers 2

2

I think Collectors.toMap() is what you're looking for:

Map<Bar, Foo> barToFoo = inputFoo.stream()
        .collect(Collectors.toMap(f -> getBar(f.barId), Function.identity()));
Sign up to request clarification or add additional context in comments.

7 Comments

What if, outside of my example, getBar returned null values and I wanted to filter them out? I would like to use .filter(Objects::nonnull) and, in general, perform more mapping etc on stuff after f ->. How can I do that?
@JohnBaum Java 9 has a filtering() collector, but in Java 8, you're probably better off sticking to regular loop.
you can wrap getBar in Optional<Bar> and filter them in the stream
What stream? once yours inside toMap, you are dealing with a function
you will need to use map instead of toMap. something like this... .filter(Optional::isPresent) .map(Optional::get)
|
1

It seems that you want a reverse map, or a map where each value points back to it's key. There are a few issues with this, the first being that a mathematical Map (sometimes represented as a function) is not guaranteed to be a mathematical Identity (which is a fancy way of saying the function has an inverse function).

So a map that takes keys of A and provides values of B looks like

f(A) -> B

But there are many maps where one cannot write it's inverse and have the inverse still be a function

f'(B) -> A (only exists for some functions)

For example

f(x) = x*x

lets one use the inputs

f(0) = 0
f(1) = 1
f(2) = 4

and also

f(-1) = 1
f(-2) = 4

so the inverse function would have to do

f'(0) = 0 (the zero's are swapped compared to the above example)
f'(1) = 1 or -1 (sorry, but that's not valid output for a function)
f'(4) = 2 or -2 (ditto)

This means that Maps, in general are not invertible. However, you could generate a generalized inverse if you relaxed the typing to an almost-inverse:

Map<Foo, Bar> x
(back maps to)
Map<Bar, Collection<Foo>> y;

The streaming API is a nice thing, but in this case it might not be providing you with the simplest solution.

Map <Bar, Set<Foo>> backmap = new HashMap<>();
for (Map.Entry<Foo, Bar> entry : barToFoo.entrySet()) {
    if (backmap.contains(entry.getValue()) {
        backmap.get(entry.getValue()).add(entry.getKey());
    } else {
        backmap.put(entry.getValue(), new HashSet<Foo>(entry.getKey));
    }
}

10 Comments

I don't think a reverse map has anything to do with the question. OP wants to map Bar to Foo and is trying to figure out how to it with a stream.
@shmosel If it starts out as a Map of Foo to Bar, then a map of Bar to Foo is a reverse map of the initial input.
Yes, but OP doesn't need a reverse map.
@shmosel What does the OP need then? I don't read the OP's mind, I read what the OP wrote. If he has a Map<Foo, Bar> and he wants [I do not know how] to get the map of Bar to Foo and that's the definition of a reverse map, then I'm at a loss as to how he hasn't asked for the reverse map.
He doesn't have a Map<Foo, Bar>. He has a Set<Foo> and wants to produce a Map<Bar, Foo> using streams.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.