Streaming the map and modifying it in Java 8 - java

I know that once we create the stream off of a collection we should not modify collection while consuming the stream, However not sure if following approach works where I am modifying the entry inside the map and not adding or removing anything from it. Also, is this the right way to stream the map always and consume it ?
creepyCategoryStatuses.entrySet().stream().forEach(entry -> {
String creepyBlockResponse = (String) getBlockResponse(entry.getKey());
if (creepyBlockResponse.equalsIgnoreCase("block")){
entry.setValue(true);
//OR creepyCategoryStatuses(entry.getKey(), true);
}
});

This kind of modification is no problem as setting the value of an entry is not a structural modification. However, it’s worth noting that there are more Java 8 features than the Stream API or the forEach method:
creepyCategoryStatuses.replaceAll((key,value)->
((String)getBlockResponse(key)).equalsIgnoreCase("block")||value
);
does the job much easier. Note that ||value will retain the old value if the condition evaluates to false as in your original code.

Yes, such usage of Map seems to be legit. Note that in this case you don't need the stream. You can simply use
creepyCategoryStatuses.entrySet().forEach(entry -> {...});

According to your use-case, you could use replaceAll here:
creepyCategoryStatuses.replaceAll((k, v) -> ((String) getBlockResponse(k)).equalsIgnoreCase("block") ? true : v);
I always favor functional paradigms, so I would collect the new data into a new map, but that's a matter of taste.

Related

Optional::isPresentOrElse -> List<> analog

Faced to some interesting issue: for example, I have to choose some solutions, depending on whether my List<> is blank or not, but I don't want to use simple if-statement for refactoring reasons (for example, there are three ugly inner if-s). If I had a nullable object, I could use the Optional::ifPresentOrElse solution, but what if the array is not null but just empty? are there any built-in language solutions? I understand that I could write something like ListOptional, create my own ListOptional::isNotEmptyOrElse there, but perhaps there is another solution?
Understand Optional as a wrapper against a possibly null instance providing chainable null-safe methods. Suggested ListOptional doesn't make to me any sense as null and empty lists semantically represent the same - a bunch of nothing.
If you want to use Optional, you must filter the single list in it whether is empty or not and provide an alternative:
List<String> nonEmpty = Optional.of(list)
.filter(l -> !l.isEmpty()) // is the list empty?
.orElseGet(() -> Arrays.asList("Hi", "Bye")); // if so, yield a default one
How is this better than using a ternary operator is questionable. I see no advantage over using this or if-else construct or a wrapper from a 3rd party library:
if (list.isEmpty() {
list = Arrays.asList("Hi", "Bye");
}
A built-in language solution for handling potentially empty collections is Stream API as declarative calls of chained methods, that are not executed on Stream from an empty collection. You can understand Stream as a wrapper against a possibly empty collection with the goal of processing each element. The same goes to the Optional with a difference a single instance is handled (regardless of whether it is a collection or not).
List<String> list = Collections.emptyList(); // empty list
List<String> updated = list.stream()
.filter(str -> str.startsWith("A") // won't happen
.map(String::toLowerCase) // won't happen
.collect(Collectors.toList()); // yields empty list

Using anonymous function with stream's map

I have a list of objects that I want to modify with one of the setters is it bad to call it in anonymous map and what are the possible side effects:
.stream().map(foo -> { foo.setDate(date);return foo;})
.collect(Collectors.toList()));
Intellij is telling me to switch it to peek
.stream().peek(foo -> foo.setDate(date).collect(Collectors.toList()));
But I read that peek should be used for debugging only. Should I avoid both ways?
Why don't you use foreach ?
.forEach(foo -> { foo.setDate(date);})
You don't even need to stream the collection.
You will save yourself the cost of creating a new collection as well.
The first way is more than OK in this case, what matters is that you don't change the source of the stream structurally, meaning adding/removing elements to it while you stream.
And indeed IntelliJ is wrong about this, a map is a lot more suited than a peek (it is only for debugging).

Assertions in Streams

I know I can filter a stream to get only those elements that are not null, and then do with them whatever I need. Something like this:
myList.stream().filter(element -> element != null).doOtherThings...
Is there a way to assert that the elements are not null in a stream function, so that it goes through all elements and if it finds one being null, it throws an exception? I've been thinking of something like the following:
myList.stream().assert(Objects::nonNull).doOtherThings...
Use something like
.map(Objects::requireNonNull)
You just need to apply an operation which will throw an exception, but otherwise passes the argument through.
Although, unless there is a good reason not to iterate the list multiple times, it might be clearer to separate the checking and the processing:
if (myList.stream().anyMatch(Objects::isNull)) {
throw ...
}
// Do things with list.
There are some very good suggestions already. Allow me to supplement. If what you are after is an assertion as in an assert statement, I would like to make this explicit in the code in order to guide the reader about my purpose. To assert that your original list doesn’t contain any nulls:
assert ! myList.contains(null);
If the assertion is to be checked somewhere down the stream pipeline, the simple way is:
assert myList.stream().map(this::transform).allMatch(Objects::nonNull);
If you don’t want to create a separate stream for the assertion but prefer to assert in the middle of your existing stream pipeline, use for example:
myList.stream()
.peek(e -> { assert e != null; })
.toArray();
You may worry that the use of peek is not so nice, which is also why I mention this option last. peek is documented to exist “mainly to support debugging” (quote taken out of its context), so you may say that it’s related to the purpose of assert and thus defend its use in this case.
To the map method I prefer to use peek method. I think it is more expressive then uing map method that has to return some value.
list.stream()
.peek(Objects::requireNonNull)
However what i think is hard to understand is that stream is not executed until the collect() is called. That is why doing this kind of conditional logic is hard using streams. To be honest if you want to achieve the desired effect you have to do something like this.
list.stream()
.peek(Objects::requireNonNull)
.collect(Collectors.toList())
.stream()
.map(t -> do whatever ... )

Finding a more functional approach using Java Optional?

I'm trying my hand at using Optional as much as I can over normal null checks; and I ran into a suggestion by my IDE (IntelliJ):
Can be replaced by a singular expression in functional style.
Here is the code in question, the line in question:
Entry entry = maybeBytes.isPresent() ? Entry.deserialize(maybeBytes.get()) : new Entry();
I've looked a bit here, but couldn't find a usage or at least see one that would fit my case here. I'm pretty new to the lambda features.
How about:
Entry entry = maybeBytes.map(Entry::deserialize).orElseGet(Entry::new);
If maybeBytes contains a value, it will be passed to the function you provide to map(), and you'll get an Optional<Entry> with the result (and if maybeBytes was empty, you'll get an empty Optional<Entry>). orElseGet() will give you the contents of the Optional<Entry> if it's nonempty, and otherwise, it will give you the result of evaluating the function you pass to it (in this case, the constructor of Entry).
Pressing Alt+Enter when you see such suggestions will apply them, and you'll see the result yourself :)
In my case it was orElse() which fit to my needs. I do not needed to do extra deserialization.
instead of isPresent() combined with get() certainly does the trick…
return maybeBytes.isPresent() ? maybeBytes.get() : new Entry();
you can use
return maybeBytes.orElse(new Entry());

Return from lambda forEach() in java

I am trying to change some for-each loops to lambda forEach()-methods to discover the possibilities of lambda expressions. The following seems to be possible:
ArrayList<Player> playersOfTeam = new ArrayList<Player>();
for (Player player : players) {
if (player.getTeam().equals(teamName)) {
playersOfTeam.add(player);
}
}
With lambda forEach()
players.forEach(player->{if (player.getTeam().equals(teamName)) {playersOfTeam.add(player);}});
But the next one doesn't work:
for (Player player : players) {
if (player.getName().contains(name)) {
return player;
}
}
with lambda
players.forEach(player->{if (player.getName().contains(name)) {return player;}});
Is there something wrong in the syntax of the last line or is it impossible to return from forEach() method?
The return there is returning from the lambda expression rather than from the containing method. Instead of forEach you need to filter the stream:
players.stream().filter(player -> player.getName().contains(name))
.findFirst().orElse(null);
Here filter restricts the stream to those items that match the predicate, and findFirst then returns an Optional with the first matching entry.
This looks less efficient than the for-loop approach, but in fact findFirst() can short-circuit - it doesn't generate the entire filtered stream and then extract one element from it, rather it filters only as many elements as it needs to in order to find the first matching one. You could also use findAny() instead of findFirst() if you don't necessarily care about getting the first matching player from the (ordered) stream but simply any matching item. This allows for better efficiency when there's parallelism involved.
I suggest you to first try to understand Java 8 in the whole picture, most importantly in your case it will be streams, lambdas and method references.
You should never convert existing code to Java 8 code on a line-by-line basis, you should extract features and convert those.
What I identified in your first case is the following:
You want to add elements of an input structure to an output list if they match some predicate.
Let's see how we do that, we can do it with the following:
List<Player> playersOfTeam = players.stream()
.filter(player -> player.getTeam().equals(teamName))
.collect(Collectors.toList());
What you do here is:
Turn your input structure into a stream (I am assuming here that it is of type Collection<Player>, now you have a Stream<Player>.
Filter out all unwanted elements with a Predicate<Player>, mapping every player to the boolean true if it is wished to be kept.
Collect the resulting elements in a list, via a Collector, here we can use one of the standard library collectors, which is Collectors.toList().
This also incorporates two other points:
Code against interfaces, so code against List<E> over ArrayList<E>.
Use diamond inference for the type parameter in new ArrayList<>(), you are using Java 8 after all.
Now onto your second point:
You again want to convert something of legacy Java to Java 8 without looking at the bigger picture. This part has already been answered by #IanRoberts, though I think that you need to do players.stream().filter(...)... over what he suggested.
If you want to return a boolean value, then you can use something like this (much faster than filter):
players.stream().anyMatch(player -> player.getName().contains(name));
This what helped me:
List<RepositoryFile> fileList = response.getRepositoryFileList();
RepositoryFile file1 = fileList.stream().filter(f -> f.getName().contains("my-file.txt")).findFirst().orElse(null);
Taken from Java 8 Finding Specific Element in List with Lambda
You can also throw an exception:
Note:
For the sake of readability each step of stream should be listed in new line.
players.stream()
.filter(player -> player.getName().contains(name))
.findFirst()
.orElseThrow(MyCustomRuntimeException::new);
if your logic is loosely "exception driven" such as there is one place in your code that catches all exceptions and decides what to do next. Only use exception driven development when you can avoid littering your code base with multiples try-catch and throwing these exceptions are for very special cases that you expect them and can be handled properly.)

Categories