java


Java 8 Stream Collectors - Collector to create a Map with objects in multiple buckets


The following code works and is readable but it seems to me I have intermediate operations that feel like they shouldn't be necessary. I've written this simplified version as the actual code is part of a much larger process.
I've got a Collection of Widget, each with a name and multiple types (indicated by constants of the WidgetType enum). These multiple types are gettable as a Stream<WidgetType> though, if necessary, I could return those as some other type. (For various reasons, it is strongly desirable that these be returned as a Stream<WidgetType> because of how these widgets are used later in the actual code.)
These widgets are added to an EnumMap<WidgetType, List<Widget>> which is, later, translated into an EnumMap<WidgetType, Widget[]>.
If each Widget only had a single WidgetType, this would be a trivial solve but, since any Widget could have 1 or more types, I am tripping all over myself with the syntax of the Collectors.groupingBy() method (and its overloads).
Here's the code example, again, fully functional and gives me the exact result I need.
class StackOverFlowExample {
private final Map<WidgetType, Widget[]> widgetMap = new EnumMap<>(WidgetType.class);
public static void main(String[] args) { new StackOverFlowExample(); }
StackOverFlowExample() {
Collection<Widget> widgetList = getWidgetsFromWhereverWidgetsComeFrom();
{
final Map<WidgetType, List<Widget>> intermediateMap = new EnumMap<>(WidgetType.class);
widgetList.forEach(w ->
w.getWidgetTypes().forEach(wt -> {
intermediateMap.putIfAbsent(wt, new ArrayList<>());
intermediateMap.get(wt).add(w);
})
);
intermediateMap.entrySet().forEach(e -> widgetMap.put(e.getKey(), e.getValue().toArray(new Widget[0])));
}
Arrays.stream(WidgetType.values()).forEach(wt -> System.out.println(wt + ": " + Arrays.toString(widgetMap.get(wt))));
}
private Collection<Widget> getWidgetsFromWhereverWidgetsComeFrom() {
return Arrays.asList(
new Widget("1st", WidgetType.TYPE_A, WidgetType.TYPE_B),
new Widget("2nd", WidgetType.TYPE_A, WidgetType.TYPE_C),
new Widget("3rd", WidgetType.TYPE_A, WidgetType.TYPE_D),
new Widget("4th", WidgetType.TYPE_C, WidgetType.TYPE_D)
);
}
}
This outputs:
TYPE_A: [1st, 2nd, 3rd]
TYPE_B: [1st]
TYPE_C: [2nd, 4th]
TYPE_D: [3rd, 4th]
For completeness sake, here's the Widget class and the WidgetType enum:
class Widget {
private final String name;
private final WidgetType[] widgetTypes;
Widget(String n, WidgetType ... wt) { name = n; widgetTypes = wt; }
public String getName() { return name; }
public Stream<WidgetType> getWidgetTypes() { return Arrays.stream(widgetTypes).distinct(); }
#Override public String toString() { return name; }
}
enum WidgetType { TYPE_A, TYPE_B, TYPE_C, TYPE_D }
Any ideas on a better way to execute this logic are welcome. Thanks!
IMHO, the key is to convert a Widget instance to a Stream<Pair<WidgetType, Widget>> instance. Once we have that, we can flatMap a stream of widgets and collect on the resulting stream. Of course we don't have Pair in Java, so have to use AbstractMap.SimpleEntry instead.
widgets.stream()
// Convert a stream of widgets to a stream of (type, widget)
.flatMap(w -> w.getTypes().map(t->new AbstractMap.SimpleEntry<>(t, w)))
// Grouping by the key, and do additional mapping to get the widget
.collect(groupingBy(e->e.getKey(),
mapping(e->e.getValue,
collectingAndThen(toList(), l->l.toArray(new Widget[0])))));
P.S. this is an occasion where IntelliJ's suggestion doesn't shorten a lambda with method reference.
This is a bit convoluted, but it produces the same output, not necessarily in the same order. It uses a static import of java.util.stream.Collectors.*.
widgetMap = widgetList.stream()
.flatMap(w -> w.getWidgetTypes().map(t -> new AbstractMap.SimpleEntry<>(t, w)))
.collect(groupingBy(Map.Entry::getKey, collectingAndThen(mapping(Map.Entry::getValue, toSet()), s -> s.stream().toArray(Widget[]::new))));
Output on my machine:
TYPE_A: [1st, 3rd, 2nd]
TYPE_B: [1st]
TYPE_C: [2nd, 4th]
TYPE_D: [3rd, 4th]

Related Links

implicit wait is not working in selenium webdriver
How can I fix charset encoding when I use RequestDispatcher include a html file?
Dropwizard not accepting #PathParam for pojo objects
Apache Velocity error in Eclipse [Java, Eclipse Luna, Velocity 2.0, VelocityView, Tomcat 7]
Failed to lazily initialize a collection of role could not initialize proxy - no Session
Unable to find explicit activity class {.CaptureActivity}; .. in manifest when calling ZXScanHelper.scan
Fresh utopic/lein/java install fails with trustAnchor issue [duplicate]
Display String based on user system language in Android
Gradle specifying sources for dependencies specified with files
Android Switch Get Text or Transistion Not OnCheckedChanged
Spring Web Services: <sws:annotation-driven /> as Java configuration
logging is not working in Spring AOP using cglib proxy
Does facebook require authentication for a trusted app?
Prevent JFrame from showing any Icon
Java Frames graphics with parameters
NamedJdbcParameterTemplate : -418 SQL error with setting Date

Categories

HOME
vbscript
netsuite
rubygems
installshield
correlation
pugjs
reactcsstransitiongroup
database-replication
object-detection
cx-freeze
functional-testing
accessor
claims-based-identity
dbclient
gsmcomm
tinymce-4
bcd
bosh
xquery-3.0
siesta-swift
webtest
framemaker
geopositioning
cloud-code
http-digest
multilingual
unoconv
g-code
ibpy
fakeiteasy
acoustics
flickr-api
lift-json
pg-dump
libusb-win32
snmptrapd
flutterwave
orthogonal
pcf
fuzzy-search
python-stackless
two-factor-authentication
jms2
metaclass
wireshark-dissector
computer-algebra-systems
coveralls
rgeo
method-parameters
census
csquery
website-monitoring
operator-precedence
html-helper
rdl
article
braille
floating-point-precision
quantlib-swig
sitemesh
google-cloud-save
shellexecute
sharp-repository
flashvars
mhtml
jquery-knob
chuck
getmessage
ticoredatasync
eclipse-templates
window-management
xtype
cxxtest
modelstate
brewmp
sef
javap
avatar
project-hosting
.net-1.0

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App