I often convert lists like that
myList.stream().map(el -> el.name).collect(Collectors.toList())
is there any shorter version for this?
There's no shorter way to do this using streams.
you could import import static java.util.stream.Collectors.*; and then use toList as follows to shorten the code a little bit but apart from this. The below is as compact as it gets using streams:
myList.stream().map(el -> el.name).collect(toList());
You could also use a method reference
myList.stream().map(T::getName).collect(toList());
where T is the name of type that contains name although this is not guaranteed to be shorter depending on how long the type name is but does provide better readability which is very important.
Ultimately, as said this is as compact as it gets.
You could create a static helper method that does all the work:
public static <FROM, TO> List<TO> convert(List<FROM> from, Function<FROM, TO> function) {
return from.stream().map(function).collect(Collectors.toList());
}
All you have to do is provide your list and any mapping function:
List<YourClass> yourList = ...;
Function<YourClass, String> func = YourClass::getName;
List<String> converted = convert(yourList, func);
Or even more concise:
List<String> converted = convert(yourList, YourClass::getName);
I think that you should stick to what you have already got. Why?
It’s already a one-liner. No real point in trying to squeeze it down further.
It’s idiomatic. Java developers are used to read conversions like yours, and if it’s all over the place in your code, programmers that read your code will be even more used to it. Even wrapping it in a method, like #QBrute suggested, even though a nice idea, risks harming readability because readers are not used to the wrapping method.
Remember: Brevity is not a goal. Clarity is. The two often go hand in hand, but not always, and my feeling is they may not in your case.
Reservation: My style and taste is in favour of the method reference that #Aomine uses, but it is a matter of taste. Use it if you find it clearer, not just because it’s a few chars shorter.
You could statically import Collectors.* and then use the mapping(Function, Collector) method, like this:
myList.stream().collect(mapping(T::getName, toList()));
Where T::getName is a method reference and T is the Type of the elements in the List. Using this is more readable and also almost identical to writing: el -> el.name
Related
I have object customerSummary at line #2 and accessing it at lines #11 & #12. Does it lead to data corruption in production?
private CustomerSummary enrichCustomerIdentifiers(CustomerSummaryDTO customerSummaryDTO) {
CustomerSummary customerSummary = customerSummaryDTO.getCustomerSummary();
List<CustomerIdentifier> customerIdentifiers = customerSummary
.getCustomerIdentifiers().stream()
.peek(customerIdentifier -> {
if (getCustomerReferenceTypes().contains(customerIdentifier.getIdentifierType())) {
customerIdentifier.setRefType(RefType.REF.toString());
} else {
customerIdentifier.setRefType(RefType.TAX.toString());
Country country = new Country();
country.setIsoCountryCode(customerSummary.getCustomerAddresses().get(0).getIsoCountryCode());
country.setCountryName(customerSummary.getCustomerAddresses().get(0).getCountryName());
customerIdentifier.setCountry(country);
}
}).collect(Collectors.toList());
customerSummary.setCustomerIdentifiers(customerIdentifiers);
return customerSummary;
}
The literal answer to your question is No ... assuming that the access is thread-safe.
But your code probably doesn't do what you think it does.
The peek() method returns the precise stream of objects that it is called on. So your code is effectively doing this:
summary.setCustomerIdentifiers(
new SomeListClass<>(summary.getCustomerIdentifiers()));
... while doing some operations on the identifier objects.
You are (AFAIK unnecessarily) copying the list and reassigning it to the field of the summary object.
It would be simpler AND more efficient to write it as:
for (CustomerIdentifier id: summary.getCustomerIdentifiers()) {
if (getCustomerReferenceTypes().contains(id.getIdentifierType())) {
id.setRefType(RefType.REF.toString());
} else {
id.setRefType(RefType.TAX.toString());
Country country = new Country();
Address address = summary.getCustomerAddresses().get(0);
country.setIsoCountryCode(address.getIsoCountryCode());
country.setCountryName(address.getCountryName());
id.setCountry(country);
}
}
You could do the above using a list.stream().forEach(), or a list.forEach(), but the code is (IMO) neither simpler or substantially more concise than a plain loop.
summary.getCustomerIdentifiers().forEach(
id -> {
if (getCustomerReferenceTypes().contains(id.getIdentifierType())) {
id.setRefType(RefType.REF.toString());
} else {
id.setRefType(RefType.TAX.toString());
Country country = new Country();
Address address = summary.getCustomerAddresses().get(0);
country.setIsoCountryCode(address.getIsoCountryCode());
country.setCountryName(address.getCountryName());
id.setCountry(country);
}
}
);
(A final micro-optimization would be to declare and initialize address outside of the loop.)
Java 8 streams are not the solution to all problems.
The direct answer to your question is a resounding 'no', but you're misusing streams, which presumably is part of why you are even asking this question. You're operating on mutables in stream code, which you shouldn't be doing: It's why I'm saying 'misusing' - this code compiles and works but leads to hard to read and had to maintain code that will fail in weird ways as you use more and more of the stream API. The solution is not to go against the grain so much.
You're also engaging in stringly based typing which is another style mistake.
Finally, your collect call is misleading.
So, to answer the question:
Does it lead to data corruption in production?
No. How would you imagine it would?
Style mistake #1: mutables
Streams don't work nearly as well when you're working with mutables. The general idea is that you have immutable classes (classes without any setters; the instances of these classes cannot change after construction. String is immutable, so is Integer, and so is BigDecimal. There is no .setValue() on an integer instance, there is no setChar() on a string, or even a clear() or an append() - all operations on immutables that appear to modify things actually return a new instance that contains the result of the operation. someBigDecimal.add() doesn't change what someBigDecimal is pointing at; it constructs a new bigDecimal instance and returns that.
With immutables, if you want to change things, Stream's map method is the right one to use: For example, if you have a stream of BigDecimal objects and you want to, say, print them all, but with 2.5 added to them, you'd be calling map: You want to map each input BigDecimal into an output BD by asking the BD instance to make a new BD instance by adding 2.5 to itself.
With mutables, both map and peek are more relevant. Style debates are rife on what to do. peek just lets you witness what's going through a stream pipeline. It can be misleading because stream pipelines dont process anything until you stick a terminator on the end (something like collect, or max() or whatnot, those are 'terminators'). When talking about mutables, peek in theory works just as well as map does and some (evidently, including intellij's auto-suggest authors) are of the belief that a map operation that really just mutates the underlying object in the stream and returns the same reference is a style violation and should be replaced with a peek operation instead.
But the far more relevant observation is that stream operations should not be mutating anything at all. Do not call setters.
You have 2 options:
Massively refactor this code, make CustomIdentifier immutable (get rid of the getters, make all fields final, consider adding with-ers and builders and the like), change your peek code to something like:
.map(identifier -> {
if (....) return customerIdentifier.with(RefType.REF);
return identifier.withCountry(new Country(summary.get..., summary.get...));
})
Note that Country also needs this treatment.
Do not use streams.
This is much simpler. This code is vastly less confusing and better style if you just write a foreach loop. I have no idea why you thought streams were appropriate here. Streams are not 'better'. A problem is that adherents of functional style are so incredibly convinced they are correct they spread copious FUD (Fear, Uncertainty, Doubt) about non-functional approaches and strongly insinuate that functional style is 'just better'. This is not true - it's merely a different style that is more suitable to some domains and less to others. This style goes a lot further than just 'turn for loops into streams', and unawareness of what 'functional style' really means just leads to hard to maintain, hard to read, weird code like what you pasted.
I really, really want to use streams here
This is just a bad idea here (unless you do the full rewrite to immutables), but if you MUST, the actual right answer is not what intellij said, it's to use forEach. This is peek and the terminal in one package. It gets rid of the pointless collect (which just recreates a list that is 100% identical to what customerSummary.getCustomerIdentifiers() returns) call and properly represents what is actually happening (which is NOT that you're writing code that witnesses what is flowing through the stream pipe, you're writing code that you intend to execute on each element in the stream).
But that's still much worse than this:
CustomerSummary summary = custumerSummaryDTO.getCustomerSummary();
for (CustomerIdentifier identifier : summary.getCustomerIdentifiers()) {
if (getCustomerReferenceTypes().contains(customerIdentifier.getIdentifierType())) {
customerIdentifier.setRefType(RefType.REF.toString());
} else {
customerIdentifier.setRefType(RefType.TAX.toString());
Country country = new Country();
country.setIsoCountryCode(customerSummary.getCustomerAddresses().get(0).getIsoCountryCode());
country.setCountryName(customerSummary.getCustomerAddresses().get(0).getCountryName());
customerIdentifier.setCountry(country);
}
}
return customerSummary;
Style mistake #2: stringly typing
Why isn't the refType field in CustomerIdentifier just RefType? Why are you converting RefType instances to strings and back?
DB engines support enums and if they don't, the in-between layer (your DTO) should support marshalling enums into strings and back.
Given a value foo and a Stream of Consumer<Foo> void functions, what's the most concise way to apply each function to the value? Right now I have
consumers.forEach(c -> c.accept(foo));
which isn't terrible, but I suspect there might be some way to turn this inside out and do it with just method references (no explicit lambda). Possibly something with a singleton list and zip()?
I'd be happy with a straight Java 8 answer, or Vavr, or Scala.
(Note that this is not a fold, or at least not the usual foldLeft/foldRight application; if there were return values, I'd be discarding them, not iterating on them.)
I think the real concern here is that methods which return void are a code smell. Why couldn't they return something useful, or at least carry out their side-effect and then return the original value, which allows for better chaining. I think that would be a preferable approach, but short of that, I think the forEach approach is ok.
You could reduce your consumers by means of the Consumer.andThen method and then apply the resulting consumer to the foo argument:
consumers.reduce(Consumer::andThen).ifPresent(c -> c.accept(foo));
But there's no difference between this approach and yours, and yours' is shorter.
In Scala you should be able to do
consumers.foreach(_.accept(foo));
There is an event class:
public class Event {
private int index;
public int getIndex() {return index;}
}
Also there is a method - it selects an event sublist with certain values of "index" property. Extremely simple, but such functionality is widely used.
public List<Event> select(List<Event> scenario, List<Integer> indexesToInclude) {
Predicate<Event> indexMatcher = e -> indexesToInclude.contains(e.getIndex());
return scenario.stream().filter(indexMatcher).collect(Collectors.toList());
}
The task is to avoid usage of -> operator in favor of :: operator. Why? Because e -> ... looks like a workaround for such common task.
Is it possible to do?
I expect syntax like (this won't compile of course):
Predicate<Event> indexMatcher = { indexesToInclude.contains(Event::getIndex) };
however it can be a chain of methods or other solution without writing loops or creating new classes/methods.
Is it possible to do?
No. Lambda expressions (the so-called "workaround") are the way to do this. That's what they were added to the language for.
(Actually ... you could do this the old-school way by defining an anonymous inner class. But it won't be a one-liner.)
Why? Because e -> ... looks like a workaround for such common task.
I guess, it depends on your perspective. For instance, a syntax purist might consider s1 + i as a "workaround" for s1.concat(Integer.toString(i)).
In fact, these things are generally called "syntactic sugar" ... and they are added to a language to make it easier to write concise and readable code.
Obviously, to be able to read the code you first need to understand the syntax, then you need to get used to it.
Unfortunately, it seems that the real problem here seems to be that you don't like the Java lambda syntax. Sorry, but you will just need to get used to it. Fighting it is not going to work.
What is
import static java.util.stream.IntStream.of;
I saw it is used to find the sum of some numbers like this,
(array of numbers).sum()
what does it do ?
What is the of method in java?
There is no of method in Java. But there is an IntStream.of method, and so on. In fact there are roughly 100 distinct of methods in the Java 8 APIs.
What is import static java.util.stream.IntStream.of; ?
It is a static import.
what does it do?
The purpose is to allow this class to refer to the static method IntStream.of as of ... with no qualification.
Without it, you would need to write:
of(<array of numbers>).sum()
as
IntStream.of(<array of numbers>).sum()
However, the latter would be the better way to write this. (IMO) Especially considering how many different of methods there are.
#Louis Wasserman comments:
What it does is make your code harder to read. Some methods just shouldn't be static imported.
I agree with that. Some people are obsessed with conciseness at the expense of maintainability. This is not an example that is good to copy.
Creates a new IntStream object with the integers you specify: https://docs.oracle.com/javase/8/docs/api/java/util/stream/IntStream.html#of-int-
For future questions make sure you refer to the Java documentation, most of the information you need is there.
This is equal to IntStream.of(array of numbers).sum(), which pipes the array of numbers into an IntStream and calls sum on the stream to evaluate the sum of its values by reduction.
I read an article on Joel On Software about the idea of using higher order functions to greatly simplify code through the use of map and reduce. He mentioned that this was difficult to do in Java. The article: http://www.joelonsoftware.com/items/2006/08/01.html
The example from the article below, loops through an array, and uses the function fn that was passed as an argument on each element in the array:
function map(fn, a)
{
for (i = 0; i < a.length; i++)
{
a[i] = fn(a[i]);
}
}
This would be invoked similar to the below in practice:
map( function(x){return x*2;}, a );
map( alert, a );
Ideally I'd like to write a map function to work on arrays, or Collections of any type if possible.
I have been looking around on the Internet, and I am having a difficult time finding resources on the subject. Firstly, are anonymous functions possible in java? Is this possible to do in another way? Will it be available in a future version of java? If possible, how can I do it?
I imagine that if this is not possible in Java there is some kind of 'pattern'/technique that people use to achieve the same effect, as I imagine anonymous functions are a very powerful tool in the software world. the only similar question I was able to find was this: Java generics - implementing higher order functions like map and it makes absolutely no sense to me.
Guava provides map (but it's called transform instead, and is in utility classes like Lists and Collections2). It doesn't provide fold/reduce, however.
In any case, the syntax for using transform feels really clunky compared to using map in Scheme. It's a bit like trying to write with your left hand, if you're right-handed. But, this is Java; what do you expect. :-P
Looks like this one?
How can I write an anonymous function in Java?
P.S: try Functional Java. Maybe it could give you hints.
Single method anonymous classes provide a similar, but much more verbose, way of writing an anonymous function in Java.
For example, you could have:
Iterable<Source> foos = ...;
Iterable<Destination> mappedFoos = foos.map(new Function<Source, Destination>()
{
public Destination apply(Source item) { return ... }
});
For an example of a Java library with a functional style, see Guava
interface Func<V,A> {
V call (A a);
}
static <V,A> List<V> map (Func<V,A> func, List<A> as) {
List<V> vs = new ArrayList<V>(as.size());
for (A a : as) {
Vs.add(func.call(a));
}
return vs;
}
Paguro has an open-source implementation of higher order functions. Initial test show it to be 98% as fast as the native Java forEach loop. The operations it supports are applied lazily without modifying the underlying collection. It outputs to type-safe versions of the immutable (and sometimes mutable) Clojure collections. Transformable is built into Paguro's unmodifiable and immutable collections and interfaces. To use a raw java.util collection as input, just wrap it with the xform() function.