I have a java.util.Map inside an rx.Observable and I want to filter the map (remove an element based on a given key).
My current code is a mix of imperative and functional, I want to accomplish this goal without the call to isItemInDataThenRemove.
public static Observable<Map<String, Object>> filter(Map<String, Object> data, String removeKey) {
return Observable.from(data).filter((entry) -> isItemInDataThenRemove(entry,removeKey));
}
private static boolean isItemInDataThenRemove(Map<String, Object> data, String removeKey) {
for (Map.Entry<String,Object> entry : data.entrySet()) {
if(entry.getKey().equalsIgnoreCase(removeKey)) {
System.out.printf("Found element %s, removing.", removeKey);
data.remove(removeKey);
return true;
}
}
return false;
}
The code you have proposed has a general problem in that it modifies the underlying stream while operating on it. This conflicts with the general requirement for streams for non-interference, and often in practice means that you will get a ConcurrentModificationException when using streams pipelines with containers that remove objects in the underlying container.
In any case (as I learned yesterday) there is a new default method on the Collection class that does pretty much exactly what you want:
private static boolean isItemInDataThenRemove(Map<String, Object> data, String removeKey) {
return data.entrySet().removeIf(entry -> entry.getKey().equalsIgnoreCase(removeKey));
}
WORKING CODE:
private static boolean isItemInDataThenRemove(Map<String, Object> data, String removeKey) {
data.entrySet().stream().filter(entry ->
entry.getKey().equalsIgnoreCase(removeKey)).forEach(entry -> {
data.remove(entry.getKey());
});
return true;
}
Related
I want to use Java 8 lambdas and streams to reduce the amount of code in the following method that produces an Optional. Is it possible to achieve?
My code:
protected Optional<String> getMediaName(Participant participant) {
for (ParticipantDevice device : participant.getDevices()) {
if (device.getMedia() != null && StringUtils.isNotEmpty(device.getMedia().getMediaType())) {
String mediaType = device.getMedia().getMediaType().toUpperCase();
Map<String, String> mediaToNameMap = config.getMediaMap();
if (mediaMap.containsKey(mediaType)) {
return Optional.of(mediaMap.get(mediaType));
}
}
}
return Optional.empty();
}
Yes. Assuming the following class hierarchy (I used records here).
record Media(String getMediaType) {
}
record ParticipantDevice(Media getMedia) {
}
record Participant(List<ParticipantDevice> getDevices) {
}
It is pretty self explanatory. Unless you have an empty string as a key you don't need, imo, to check for it in your search. The main difference here is that once the map entry is found, Optional.map is used to return the value instead of the key.
I also checked this out against your loop version and it works the same.
public static Optional<String> getMediaName(Participant participant) {
Map<String, String> mediaToNameMap = config.getMediaMap();
return participant.getDevices().stream()
.map(ParticipantDevice::getMedia).filter(Objects::nonNull)
.map(media -> media.getMediaType().toUpperCase())
.filter(mediaType -> mediaToNameMap.containsKey(mediaType))
.findFirst()
.map(mediaToNameMap::get);
}
Firstly, since your Map of media types returned by config.getMediaMap() doesn't depend on a particular device, it makes sense to generate it before processing the collection of devices. I.e. regurless of the approach (imperative or declarative) do it outside a Loop, or before creating a Stream, to avoid generating the same Map multiple times.
And to implement this method with Streams, you need to use filter() operation, which expects a Predicate, to apply the conditional logic and map() perform a transformation of stream elements.
To get the first element that matches the conditions apply findFirst(), which produces an optional result, as a terminal operation.
protected Optional<String> getMediaName(Participant participant) {
Map<String, String> mediaToNameMap = config.getMediaMap();
return participant.getDevices().stream()
.filter(device -> device.getMedia() != null
&& StringUtils.isNotEmpty(device.getMedia().getMediaType())
)
.map(device -> device.getMedia().getMediaType().toUpperCase())
.filter(mediaToNameMap::containsKey)
.map(mediaToNameMap::get)
.findFirst();
}
I want to do a get request and want to get all the jobs who have the "teacher" in it. In this case the map value 2 and 3.
How do I get there if I execute the code below then I only get the value from the Map 2. The best way is over a List? But how do I do it?
I have this method.
#GET
#Path("jobs")
public Job getJobsId(#QueryParam("id") int id, #QueryParam("description") String description) {
for (final Map.Entry<Integer, Job> entry : jobs.entrySet()) {
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase())) {
System.out.println("I DID IT");
System.out.println(entry.getKey());
return berufe.get(entry.getKey());
}
}
return berufe.get(id);
}
and this Map:
jobs.put(1, new jobs(1, 1337, "student"));
jobs.put(2, new jobs(2, 420, "teacher"));
jobs.put(3, new jobs(3, 69, "schoolteacher"));
---------------------------------EDIT----------------------------------
If I do this:
#GET
#Path("jobs")
public Collection<Job> getJobsId(#QueryParam("id") int id, #QueryParam("description") String description) {
final Set<Beruf> result = new HashSet<>();
for (final Map.Entry<Integer, Job> entry : jobs.entrySet()) {
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase()) == true) {
result.add(jobs.get(entry.getKey()));
} else {
return jobs.values();
}
}
return result;
}
I get with a discription all Map values back and without I get an Error.
What do I do wrong here?
Your method getJobsId() returns on the first item it finds (inside the if statement), that's why you only get one result.
The usual pattern to collect a number of results would be to instantiate a suitable Collection before the for loop, and add each item that is found to this collection. Then, after the for loop, return the collection. I don't understand your code completely, but it would be something similar to the below (I'm sure it won't work if you just copy-paste, so read and understand what is going on ;-) ):
Set<Beruf> result = new HashSet<>();
for (final Map.Entry<Integer, Job> entry : jobs.entrySet()) {
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase())) {
result.add(berufe.get(entry.getKey()));
}
}
return result;
Since Java 8, it has been much more concise (readable) to use the Streams interface and call .filter (and maybe .map) on it, then .collect the stream to a suitable collection and return that. Something similar to:
return jobs.entrySet().stream()
.filter(entry -> (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase())))
.map(entry -> berufe.get(entry.getKey()))
.collect(Collectors.toSet());
A function can return only one element. If there more than one hit, you have to return an object that can take on multiple elements. As you already mentioned a list, as an representant of a collection, can do this job. For your use case is a map a better option:
#GET
#Path("jobs")
public Job getJobsId(#QueryParam("id") int id, #QueryParam("description") String description)
{
//Declare an object that can take on multiple elements
var Map<Job> jobList = new HashMap<>();
for (final Map.Entry<Integer, Job> entry : jobs.entrySet())
{
if (entry.getValue().getDescription().toLowerCase().contains(description.toLowerCase()))
{
System.out.println("I DID IT");
System.out.println(entry.getKey());
jobList.put(entry.getKey(), job.get(entry.getKey()));
}
}
return jobList.get(id);
}
First of all, you should do is design the API correctly, for example:
#GET
#Path("jobs")
public List<Integer> searchJobsIdByDescription(#QueryParam("description") String description) {
...
}
Important: you must give a suitable name to the method (f.e: searchJobsIdByDescription), your method should only do one thing, so you declare only the parameters needed to do that thing (f.e: (#QueryParam("description") String description)) and return the expected type (List<Integer>).
Pay attention to the annotation #Path("jobs"), you have to avoid matching the previous ones
Next, you should carefully read #frIV's answer
I have following use case.
There is one machine which is sending event streams to Kafka which are being received by CEP engine where warnings are generated when conditions are satisfied on the Stream data.
FlinkKafkaConsumer011<Event> kafkaSource = new FlinkKafkaConsumer011<Event>(kafkaInputTopic, new EventDeserializationSchema(), properties);
DataStream<Event> eventStream = env.addSource(kafkaSource);
Event POJO contains id, name, time, ip.
Machine will send huge data to Kafka and there are 35 unique event names from machine (like name1, name2 ..... name35) and I want to detect patterns for each event name combination (like name1 co-occurred with name2, name1 co-occurred with name3.. etc). I got totally 1225 combinations.
Rule POJO contains e1Name and e2Name.
List<Rule> ruleList -> It contains 1225 rules.
for (Rule rule : ruleList) {
Pattern<Event, ?> warningPattern = Pattern.<Event>begin("start").where(new SimpleCondition<Event>() {
#Override
public boolean filter(Event value) throws Exception {
if(value.getName().equals(rule.getE1Name())) {
return true;
}
return false;
}
}).followedBy("next").where(new SimpleCondition<Event>() {
#Override
public boolean filter(Event value) throws Exception {
if(value.getName().equals(rule.getE2Name())) {
return true;
}
return false;
}
}).within(Time.seconds(30));
PatternStream patternStream = CEP.pattern(eventStream, warningPattern);
}
Is this correct way to execute multiple patterns on one stream data or is there any optimized way to achieve this. With above approach we are getting PartitionNotFoundException and UnknownTaskExecutorException and memory issues.
IMO you don't need patterns to achieve your goal. You can define a stateful map function to the source, which maps event names as pairs (latest two names). After that, window the source to 30 seconds and apply the simple WordCount example to the source.
Stateful map function can be something like this (accepting only event name, you need to change it according to your input -extract event name etc.):
public class TupleMap implements MapFunction<String, Tuple2<String, Integer>>{
Tuple2<String, String> latestTuple = new Tuple2<String, String>();
public Tuple2<String, Integer> map(String value) throws Exception {
this.latestTuple.f0 = this.latestTuple.f1;
this.latestTuple.f1 = value;
return new Tuple2<String, Integer>(this.latestTuple.f0 + this.latestTuple.f1, 1);
}
}
and result with event name pairs and occurrence count as a tuple can be obtained like this (written to a kafka sink maybe?):
DataStream<Tuple2<String, Integer>> source = stream.map(new TupleMap());
SingleOutputStreamOperator<Tuple2<String, Integer>> sum = source.keyBy(0).timeWindow(Time.seconds(30)).sum(1);
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Maps with multiple types of values in java
I have an odd question. Maybe I'm going about this the wrong way, but let's see where this question goes :)
I would like a Map container that contains either Strings or lists of Strings. I want to enforce this rule during construction of this object so that you can't create a map with values that aren't either of those.
e.g.
class Record {
public Record(String key, Map<String,Object> attrs) {
// check that attrs only contains Objects which are Strings or List<Strings>
}
}
Other ways I have thought of to solve the problem might be...
1)
class Record {
public Record(String key, Map<String,String> attrs, Map<String,List<String>> multiAttrs) {
// ...
}
}
2)
class Record {
public Record(String key, Map<String,Value> attrs) {
// ...
}
}
class Value {
// Create some funky class that encapsulates lists.
// Perhaps returning the only element in the list if the size is 1,
// but returning the list otherwise
}
I am not immediately excited at the alternatives, but I'm just putting it there as stuff I've already considered. Really I want the distinction between Strings and List to be transparent to the user of the class.
Have you considered ListMultimap? For the single value case the list would only have one element. Multimap allows multiple elements (values) to be mapped to each key. So your method would be:
public Record(String key, ListMultimap<String, String> attrs)...
Also, since your Record seems to be another mapping, consider using Table which allows for two-key mapping.
Check out ArrayListMultimap from Google which will help with this need
You can continue calling put on this map, if you need to get the map in its simplified form you can use this method, or modify it :)
public static Map<Field, String> toSingularMap(ArrayListMultimap<Field, String> map) {
Map<Field, String> singular_map = new HashMap<Field, String>();
if (map != null && !map.isEmpty()) {
Map<Field, Collection<String>> real_map = map.asMap();
for (Iterator<Entry<Field, Collection<String>>> it = real_map
.entrySet().iterator(); it.hasNext();) {
Entry<Field, Collection<String>> entry = it.next();
Field field = entry.getKey();
Collection<String> values = entry.getValue();
String value = null;
if (values != null && !values.isEmpty()) {
ArrayList<String> list = new ArrayList<String>(values);
value = list.get(0);
}
singular_map.put(field, value);
}
}
return singular_map;
}
Or if you do not want to use an extra library, you can create a simple Wrapper class
class Wrap {
String value;
String[] values
}
and have your map use Map<String, Wrap> map, when looping you can then determine either through use of your class methods or just testing, which one of the Wrapper variables are populated
I would use only List<String>. You could maybe add some methods to allow adding a single String and wrap the passed argument using Arrays.asList(...). Using only a single type of objects will reduce the quantity of code to write and avoid many if/else.
Why not create a class
class MyFunkyValue{
private String onlyOneString;
private List<String> stringValues;
public MyFunkyValue(String s){
...
}
public MyFunkyValue(List<String>ls){
...
}
}
and use it like this:
Map<KeyClass,MyFunkyValue> m;
I have a question about simplifying some Collection handling code, when using Google Collections (update: Guava).
I've got a bunch of "Computer" objects, and I want to end up with a Collection of their "resource id"s. This is done like so:
Collection<Computer> matchingComputers = findComputers();
Collection<String> resourceIds =
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() {
public String apply(Computer from) {
return from.getResourceId();
}
}));
Now, getResourceId() may return null (and changing that is not an option right now), yet in this case I'd like to omit nulls from the resulting String collection.
Here's one way to filter nulls out:
Collections2.filter(resourceIds, new Predicate<String>() {
#Override
public boolean apply(String input) {
return input != null;
}
});
You could put all that together like this:
Collection<String> resourceIds = Collections2.filter(
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() {
public String apply(Computer from) {
return from.getResourceId();
}
})), new Predicate<String>() {
#Override
public boolean apply(String input) {
return input != null;
}
});
But this is hardly elegant, let alone readable, for such a simple task! In fact, plain old Java code (with no fancy Predicate or Function stuff at all) would arguably be much cleaner:
Collection<String> resourceIds = Lists.newArrayList();
for (Computer computer : matchingComputers) {
String resourceId = computer.getResourceId();
if (resourceId != null) {
resourceIds.add(resourceId);
}
}
Using the above is certainly also an option, but out of curiosity (and desire to learn more of Google Collections), can you do the exact same thing in some shorter or more elegant way using Google Collections?
There's already a predicate in Predicates that will help you here -- Predicates.notNull() -- and you can use Iterables.filter() and the fact that Lists.newArrayList() can take an Iterable to clean this up a little more.
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(
Iterables.transform(matchingComputers, yourFunction),
Predicates.notNull()
)
);
If you don't actually need a Collection, just an Iterable, then the Lists.newArrayList() call can go away too and you're one step cleaner again!
I suspect you might find that the Function will come in handy again, and will be most useful declared as
public class Computer {
// ...
public static Function<Computer, String> TO_ID = ...;
}
which cleans this up even more (and will promote reuse).
A bit "prettier" syntax with FluentIterable (since Guava 12):
ImmutableList<String> resourceIds = FluentIterable.from(matchingComputers)
.transform(getResourceId)
.filter(Predicates.notNull())
.toList();
static final Function<Computer, String> getResourceId =
new Function<Computer, String>() {
#Override
public String apply(Computer computer) {
return computer.getResourceId();
}
};
Note that the returned list is an ImmutableList. However, you can use copyInto() method to pour the elements into an arbitrary collection.
It took longer than #Jon Skeet expected, but Java 8 streams do make this simple:
List<String> resourceIds = computers.stream()
.map(Computer::getResourceId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
You can also use .filter(x -> x != null) if you like; the difference is very minor.
Firstly, I'd create a constant filter somewhere:
public static final Predicate<Object> NULL_FILTER = new Predicate<Object>() {
#Override
public boolean apply(Object input) {
return input != null;
}
}
Then you can use:
Iterable<String> ids = Iterables.transform(matchingComputers,
new Function<Computer, String>() {
public String apply(Computer from) {
return from.getResourceId();
}
}));
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(ids, NULL_FILTER));
You can use the same null filter everywhere in your code.
If you use the same computing function elsewhere, you can make that a constant too, leaving just:
Collection<String> resourceIds = Lists.newArrayList(
Iterables.filter(
Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION),
NULL_FILTER));
It's certainly not as nice as the C# equivalent would be, but this is all going to get a lot nicer in Java 7 with closures and extension methods :)
You could write your own method like so. this will filter out nulls for any Function that returns null from the apply method.
public static <F, T> Collection<T> transformAndFilterNulls(List<F> fromList, Function<? super F, ? extends T> function) {
return Collections2.filter(Lists.transform(fromList, function), Predicates.<T>notNull());
}
The method can then be called with the following code.
Collection c = transformAndFilterNulls(Lists.newArrayList("", "SD", "DDF"), new Function<String, Long>() {
#Override
public Long apply(String s) {
return s.isEmpty() ? 20L : null;
}
});
System.err.println(c);