Let's assume that we have a List of ExampleDTO that contains some fields, among others "name" field. Everytime I want to get a list of proper fields, in this case names, I write code as below:
private List<String> getNames(List<ExampleDTO> exampleDTOs) {
List<String> names = new ArrayList<String>();
for (ExampleDTO exampleDTO : exampleDTOs)
names.add(exampleDTO.getName());
return names;
}
...but I also noticed that this kind of code is repeating over and over again.
And the question is: Is there any smart method that would help me reduce amount of redundant code produced?
EDIT:
I'm using Java7 for my application.
With Java 7 you can use Guava's FluentIterable and Function:
List<ExampleDTO> exampleDTOs = ...;
List<String> names = FluentIterable.from(exampleDTOs)
.transform(new Function<ExampleDTO, String>() {
#Override
public String apply(ExampleDTO dto) {
return dto.getName();
}
})
.toList();
Note that, although the whole loop is expressed as a single expression, the performance is significantly worse than a loop, and it's very arguable if this is more readable.
I would stick to a simple loop until the upgrade to Java 8.
In one of the projects, I used to mark all such loops with special comments, so that all these places were easy to find when the upgrade to Java 8 was performed.
With Java 8 you could use Streams API:
List<ExampleDTO> exampleDTOs = ...;
List<String> names = exampleDTOs.stream()
.map(dto -> dto.getName())
.collect(Collectors.toList());
It could get even shorter if you don't necessarily need the list of names — in this case the .collect(Collectors.toList()) part could be removed.
For example, you can iterate over the list of names directly on the stream as follows:
exampleDTOs.stream()
.map(dto -> dto.getName())
.forEach(name -> System.out.println(name));
This would just print all the names, but you can replace the System.out.println(name) with anything else.
In java 8 you have a new interface called Function allowing you to map a value to get something else, the idea here would be to implement the same logic with Java 7.
So the function interface would be:
public interface Function<T, R> {
/**
* Applies this function to the given argument.
* #param t the function argument
* #return the function result
*/
R apply(T t);
}
and your method could be something like that:
private <R> List<R> getValues(List<ExampleDTO> exampleDTOs, Function<ExampleDTO, R> function) {
List<R> values = new ArrayList<>();
for (ExampleDTO exampleDTO : exampleDTOs)
values.add(function.apply(exampleDTO));
return values;
}
In your case you could use a private static final variable to define your function as an anonymous class, like this:
private static final Function<ExampleDTO, String> BY_NAME = new Function<ExampleDTO, String>() {
public String apply(ExampleDTO e) {
return e.getName();
}
}
Then calls getValues(exampleDTOs, BY_NAME)
Related
In C#, it is relatively straight forward to retrieve items from an array of given a specific type as a generic method parameter using Linq:
class SimpleIoC
{
object[] registrations = new object[] {
new ServiceA(), new ServiceB(), new ServiceC(), new ServiceA()
};
public IEnumerable<T> GetAll<T>() => registrations.OfType<T>();
}
var ioc = new SimpleIoC();
var serviceAs = ioc.GetAll<ServiceA>();
Is this achievable in Java? If so, how?
#Test
public void Testing_stuff() {
ArrayList<Receives<?>> receivers = new ArrayList<>();
receivers.add(new TestReceiver("I picked a bad day to give up learning java..."));
Iterable<Receives<TestMessage>> all = getTheDarnThing(receivers);
}
private <T> Iterable<T> getTheDarnThing(ArrayList<?> list) {
// help me obi-wan kenobi...
return list.stream()
.filter(x -> T.class.isAssignableFrom(x.getClass()))
.map(x -> (T) x) // unchecked
.collect(Collectors.toList());
}
Also, is it possible to know what the type of T is for the generic parameter?
In Java you need to pass something to identify the type as a parameter to the method, because type parameters like T are only a compile time construct. Often a Class object is used:
private <T> List<T> getTheDarnThing(List<?> list, Class<T> klass) {
return list.stream()
.filter(klass::isInstance)
.map(klass::cast)
.collect(Collectors.toList());
}
This is how you can use it:
List<Service> services = List.of(new ServiceA(), new ServiceB(), new ServiceC());
List<ServiceA> as = getTheDarnThing(services, ServiceA.class);
Note that in your Java code, all objects in your list are instances of Receives class with different type parameters. You won't be able to tell two of these objects apart at run time, since the type parameter is erased. What you can do is define specialized classes to represent the different types of "Receives" objects. For example: class TestReceives extends Receives<TestMessage>
Given:
public abstract class Cars {}
...
public class Ford extends Cars {}
...
public class Dodge extends Cars {}
...
public class Volkswagen extends Cars {}
...
If I have two ArrayList objects:
List<Cars> dealer1 = new ArrayList<>;
List<Cars> dealer2 = new ArrayList<>;
dealer1.addAll(asList(new Ford("ford1"), new Dodge("dodge1")));
dealer2.addAll(asList(new Dodge("dodge2"), new Volkswagen("vw1")));
I then want to create a merged list from the two with only one instance of each subclass, such that:
dealerMerged = ["ford1", "dodge1", "vw1"]
OR
dealerMerged = ["ford1", "dodge2", "vw1"]
It doesn't matter which instance makes it into the merged list.
Is this possible? I had a search through and saw something about using Set but that seems to only ensure unique references, unless I've badly misunderstood something.
Overriding equals() will work but DON'T
You can always make your collection distinctful converting it to a Set (as #Arun states in comment) or using distinct operation over the Stream of your collections. But remember those approaches use the equal() methods for that. So a quick thinking would be overriding equals() and return its Class type. But wait ! If you do so you will end up having all Dodge objects equals to each other despite they have different properties like name dodge1, dodge2. You may not only handle a single business in read world. equal() method has lots of other significances. So stay away of doing so.
If you are thinking a Java 8 way, Stateful Filter is perfect
We have a choice to use the filter operation for our concatenated stream. filter operation works pretty straight forward. It takes a predicate and decide which element to take or ignore. This a commonly used function that you will find all over the blogs that solves this problem.
public static <T> Predicate<T> distinctBy(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
Here the distinctBy function returns a predicate (that will be used in filter operation). It maintains state about what it's seen previously and returns whether the given element was seen for the first time. (You can read further explanation about this here)
You can use this Stateful Filter like
Stream.of(dealer1, dealer2)
.flatMap(Collection::stream)
.filter(distinctBy(Cars::getClass))
.collect(Collectors.toList())
.forEach(cars -> System.out.println(cars));
So What we actually did here ?
We concatenated the 2 ArrayList with flatmap that will give us a single stream of the merged elements (If you are new to Stream API. See this Stream Concatenation article
We then exploits the filter() operation that is feed with the distinctBy method which return a predicate.
And you see a ConcurrentHashMap is maintained to track which element satisfies the predicate or not by a boolean flag.
And the predicate uses the getClass() method which returns the full class name, that distinguise the elements as subclasses
We then can collect or iterate over the filtered list.
Try using Map instead of List. You may please try following solution. This will let you put Car instances by their types. Thereby you will always have only one entry per class (this will be the latest entry in your map by the way).
public class CarsCollection {
Map<Class<? extends Cars>, ? super Cars> coll = new HashMap<>();
public <T extends Cars> void add(Class<T> cls, T obj) {
coll.put(cls, obj);
}
}
public class Temp {
public static void main(String[] args) {
CarsCollection nos1 = new CarsCollection();
cars.add(Ford.class, new Ford("ford1"));
cars.add(Dodge.class, new Dodge("dodge1"));
cars.add(Dodge.class, new Dodge("dodge2"));
cars.add(Volkswagen.class, new Volkswagen("vw1"));
System.out.println(cars);
}
}
You could add all the element of the first list into the result list (assuming there is no duplicate in the first list) and then loop through the second list and add the elements to the resulting list only if there is no instance of the same class in the first list.
That could look something like this :
dealerMerged = dealer1;
boolean isAlreadyRepresented;
for (car2 : dealer2) {
isAlreadyRepresented = false;
for (car1 : dealer1) {
if (car1.getClass().equals(car2.getClass())) {
isAlreadyRepresented = true;
}
}
if (!isAlreadyRepresented) {
dealerMerged.add(car2);
}
}
Just use class of the object as key in the map. This example with Java stream does exactly that:
List<Cars> merged = Stream.of(dealer1, dealer2)
.flatMap(Collection::stream)
.collect( Collectors.toMap( Object::getClass, Function.identity(), (c1, c2) -> c1 ) )
.values()
.stream().collect( Collectors.toList() );
I have the following situation:
public ArrayList<A> getMethods(){
return b.c.test();
}
So, my problem is that b.c.test() returns a value with Optional<A> as return type. But I need to return an ArrayList<A>.
So, I tried to cast it and rewrite it to :
public ArrayList<A> getMethods(){
return (ArrayList<A>)b.c.test();
}
But Eclipse says that such a cast from Optional<A> to ArrayList<A> is not possible.
How can I solve this problem?
I am presuming your intended semantic is 'if the value is present return a list with a single item, otherwise return an empty list.' In that case I would suggest something like the following:
ArrayList<A> result = new ArrayList<>();
b.c.test().ifPresent(result::add);
return result;
However I would suggest your return type should be List<A> rather than ArrayList<A> as that gives you the opportunity to change the type of list without changing the callers. It would also allow you to return Collections.EMPTY_LIST if the optional value is not present which is more efficient than creating an unnecessary ArrayList.
Update: there's now an easier option with Java 9:
b.c.test().stream().collect(Collectors.toList());
Update: and even easier option with Java 16:
b.c.test().stream().toList();
In this case, it is possible to not use streams at all:
public static <T> List<T> toList(Optional<T> opt) {
return opt.isPresent()
? Collections.singletonList(opt.get())
: Collections.emptyList();
}
Or, the same code using the functional API:
public static <T> List<T> toList(Optional<T> opt) {
return opt
.map(Collections::singletonList)
.orElseGet(Collections::emptyList);
}
I prefer the upper variant because I know for sure that it doesn't create any unnecessary objects on the Java heap.
If everyone insists on using streams for this issue, it should be more idiomatic than using ifPresent()
Unfortunately, Java 8 does not have a Optional.stream() method, so it is not possible to do:
optional.stream().collect(Collectors.toList());
see also: Using Java 8's Optional with Stream::flatMap
But in JDK 9, it will be added (and that code actually already runs on Java 9)
Optional<Integer> o = Optional.empty();
final List<Integer> list = o.stream().collect(Collectors.toList());
System.out.println(list);
return b.c.test()
.map(Arrays::asList).map(ArrayList::new)
.orElseGet(ArrayList::new);
If the optional has a value, it "maps" it to a List<A> with Arrays.asList and then to an ArrayList via the new ArrayList<A>(List<A>) constructor; otherwise it yields an empty ArrayList via the empty constructor.
This could be more explicitly written out as:
return b.c.test()
.map(value -> new ArrayList<A>(Arrays.asList(value)))
.orElseGet(() -> new ArrayList<A>());
With Java9, you can do this using the newly added Optional::stream API :
Optional<List<A>> list;
List<A> collect = list.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
Since Java 9
return b.c.test().stream()
.collect(toList());
import static java.util.stream.Collectors.toList;
Since Java 8
return b.c.test()
.map(List::of)
.orElse(emptyList());
import static java.util.Collections.emptyList;
If you're using Guava's Optional, you can do:
return new ArrayList<>(b.c.test().asSet());
This will extract the value from the Optional (if present) and add it to a new ArrayList.
If you're using Java 8, #sprinter's answer is what you need.
An Optional is a container object which may or may not contain a non-null value.
In ArrayList terms I would translate it as an array which has 0 or 1 members.
public ArrayList<A> getMethods(){
Optional<A> opt = b.c.test();
ArrayList<A> res = new ArrayList<>();
if ( opt.isPresent() )
res.add( opt.get() );
return res;
}
I have multiple Set<String> that I need to merge into one Set<String>. How do I do this operation in Java? Note, I am using using the guava API as best as I can to help out. For example, I have 3 classes as follows.
public class One {
public static Set<String> SET = Sets.newHashSet("a","b","c");
}
public class Two {
public static Set<String> SET = Sets.newHashSet("a","d","e","f");
}
public class Three {
public static Set<String> SET = Sets.newHashSet("w","x","y","f");
}
Now, I need to merge any combination of these sets into one. For, example, I may need to merge
One.SET + Two.SET + Three.SET into one to produce { "a","b","c","d","e","f","w","x","y" },
One.SET + Three.SET into one to produce { "a","b","c","w","x","y","f" },
Two.SET + Three.SET into one to produce { "a","d","e","f","w","x","y" },
and so on
I created a method to merge an array of sets, Set<String>[], but that doesn't work (explained here by this SO post Creating an array of Sets in Java). Here's the code to merge. It works (compiles).
public static Set<String> immutableSetOf(Set<String>[] sets) {
Set<String> set = new HashSet<String>();
for(Set<String> s : sets) {
set.addAll(s);
}
return ImmutableSet.copyOf(set);
}
Here's the calling code; it doesn't work (doesn't compile).
Set<String> set = Utils.immutableSetOf(new Set<String>[] { One.SET, Two.SET });
So, I modified my merging method to operate on List<Set<String>> instead of Set<String>[]. Only the argument type changed, but I put it here for completeness.
public static Set<String> immutableSetOf(List<Set<String>> sets) {
Set<String> set = new HashSet<String>();
for(Set<String> s : sets) {
set.addAll(s);
}
return ImmutableSet.copyOf(set);
}
So, now my calling code looks like the following.
Set<String> set = Utils.immutableSetOf(
Lists.newArrayList(
One.SET, Two.SET));
This code does not compile, since Lists.newArrayList(...) is returning Set<String> and not List<Set<String>>. The method Lists.newArrayList(...) is overloaded, and the signature of the method that is used when I pass in sets is, List.newArrayList(Iterable<? extends E> elements).
So, the question is, how do I define a method to merge an arbitrary number of Set<String> while considering the calling code? I note that the compilation problems are on the calling code (not the merging method), but perhaps the solution also relates to the merging code?
Update: I also tried varargs but that produces its own warning (Is it possible to solve the "A generic array of T is created for a varargs parameter" compiler warning?). The merging method signature is now the following.
public static Set<String> immutableSetOf(Set<String>... sets)
The calling code is now the following, and I get "Type safety: A generic array of Set is created for a varargs parameter".
Set<String> set = Utils.immutableSetOf(One.SET, Two.SET);
Update: For the accepted answer, I did the following.
#SuppressWarnings("unchecked")
Set<String> set = Utils.immutableSetOf(Set[] { One.SET, Two.SET });
Recommend com.google.common.collect.Sets#union(set1, set2) to get the merge instead of Set.addAll under hood, since Guava is already in your dependencies.
Reason: it's view which is memory effective, and also unmodifiable.
plus: I should have post it as a comment, sorry.
How about creating a collection of your input sets, then using flatMap?
Set<String> allElements = ImmutableSet.of(
One.SET,
Two.SET,
Three.SET
).stream().flatMap(Collection::stream).collect(Collectors.toSet());
In your previous attempt, you had written
Set<String> set = Utils.immutableSetOf(new Set<String>[] { One.SET, Two.SET });
This does not work because, generics array creation is not allowed in java
Try this:
#SuppressWarnings("unchecked")
Set<String>[] mySet = new Set[] { One.SET, Two.SET };
Set<String> set = Utils.immutableSetOf(mySet);
The reason this works is because, we create a new Set[] without specifying the generics type and assign it to the reference Set[] mySet (since this is an unchecked operation, we have to add #SuppressWarnings("unchecked") to it)
You could use:
Set<String> set = immutableSetOf(Arrays.asList(One.SET, Two.SET));
Using your definition of immutableSetOf(List<Set<String>> sets). No warnings or suppressed warnings.
I'm thinking that FluentIterable (in 18.0 and above) can help here, specifically the append method.
With that, we need to define a convenient helper method - and yes, we're going to use varargs for this.
public <T extends Comparable<T>> Set<T> mergeSets(Set<T> initial,
Set<T>... rest) {
FluentIterable<T> result = FluentIterable.from(initial);
for(Set<T> set : rest) {
result = result.append(set);
}
return new TreeSet<>(result.toSet());
}
The resultant set here is in natural order. If you don't want a TreeSet and you don't want to mutate your collection afterwards, then omit the new TreeSet<> piece, and loosen the bound on T.
Let's say I have a bean like below.
class Customer{
private String code;
private String name;
private Integer value;
//getters setters omitted for brevity
}
Then from a method I get a List<Customer> back. Now let's say I want to get a list of all member "name" from the List. Obviously I can traverse and build a List<String> of element "name" myself.
However, I was wondering if there is a short cut or more effiecient way to this technique that anyone knows . For instance, if I want to get a list of all keys in a Map object I get do map.keySet(). Something along that line is what I am trying to find out.
Guava has Lists.transform that can transform a List<F> to a List<T>, using a provided Function<F,T> (or rather, Function<? super F,? extends T>).
From the documentation:
public static <F,T>
List<T> transform(
List<F> fromList,
Function<? super F,? extends T> function
)
Returns a list that applies function to each element of fromList. The returned list is a transformed view of fromList; changes to fromList will be reflected in the returned list and vice versa.
The function is applied lazily, invoked when needed.
Similar live-view transforms are also provided as follows:
Iterables.transform (Iterable<F> to Iterable<T>)
Iterators.transform (Iterator<F> to Iterator<T>)
Collections2.transform (Collection<F> to Collection<T>)
Maps.transformValues (Map<K,V1> to Map<K,V2>)
Looks like you're looking for the Java equivalent of Perl's map function. This kind of thing might be added to the collections library once (if) Java receives closures. Until then, I think this is the best you can do:
List<String> list = new ArrayList<String>(customers.size());
for ( Customer c : customers ) {
list.add(c.getName());
}
You could also write a map function that uses a simple interface to provide the mapping function. Something like this:
public interface Transform<I, O> {
O transform(I in);
}
public <I, O> List<O> map(Collection<I> coll, Transform<? super I, ? extends O> xfrm) {
List<O> list = new ArrayList<O>(coll.size());
for ( I in : coll ) {
list.add(xfrm.transform(in));
}
return list;
}
could use something like this:
http://code.google.com/p/lambdaj/
I think this is something that you would have to code yourself, in a loop.
You can use LambdaJ's Converter interface and have the following line:
List<String> customerNames = convert(customerList, new Converter<Customer,String>() {
public String convert(Customer customer) {
return customer.getName();
}
});
You need to use a loop, but the function you're looking for is called map in functional languages. It's possible to implement map in Java, although it tends to be fairly inelegant; here's the version I implemented ages ago in my "stuff Java should have but for some reason doesn't" library:
public interface MapFunction<T, U> {
public U map(T source);
}
public static <T, U> U[] map(T[] objects, MapFunction<T, U> f) {
if(objects.length == 0) {throw new IllegalArgumentException("Can't map onto an empty array");}
#SuppressWarnings("unchecked") U[] rtn = (U[])Array.newInstance(f.map(objects[0]).getClass(), objects.length);
for(int i = 0; i < objects.length; i++)
rtn[i] = f.map(objects[i]);
return rtn;
}
Using that, you could do:
List<Customer> list = yourFunction();
List<String> names = Arrays.asList(map(list.toArray(new Customer[0]), new MapFunction<Customer, String>() {
public String map(Customer c) {
return c.getName();
}
}));
You could naturally change map to take collections instead of arrays, which would eliminate the need for Arrays.asList and List.toArray
Using Guava, you can use a Function along with Iterables.transform, Collections2.transform or Lists.transform to create an Iterable, Collection or List respectively.
Iterable<String> names = Iterables.transform(customers,
new Function<Customer, String>() {
public String apply(Customer from) {
return from.getName();
}
});
The returned Iterable is lazy and applies the function to the underlying list as you iterate through it. For a List<String> containing the names, you could use:
List<String> names = Lists.transform(...);
or
ImmutableList<String> names = ImmutableList.copyOf(Iterables.transform(...));
Of course, writing out the anonymous inner class Function implementation each time you want to do this is ugly and verbose, so you may want to make the Function a constant available from the Customer class, called Customer.NAME for instance.
Then the transformation looks much nicer (with static imports especially):
for (String name : transform(customers, Customer.NAME)) { ... }
I also wrote about using interfaces for particular properties of objects (such as name here) to help with consolidating such functions on my blog here.
.... is there a short cut or more efficient way
So, are you looking for a more efficient way to do this:
List<String> names = new ArrayList<String>();
for( Customer customer : yourCustomerList ) {
names.add( customer.getName() );
}
?!!!!
Or just a different way?
All the previous answer are not really more efficient in terms of runtime nor coding. They are however more flexible without a doubt.
Another alternative would be to include Scala Groovy in your Java code and use this:
list.map( _.name )
list.collect { it.name }
If compiled, Groovy classes may be used from Java, or you can plug in them as an script.
Here's a sample for the given Customer class using Groovy as script.
List<Customer> customers = Arrays.asList( new Customer[]{
new Customer("A","123",1),
new Customer("B","456",2),
new Customer("C","789",3),
new Customer("D","012",4)
});
setVariable(customers, "list");
evaluate("names = list.collect { it.name } ");
List<String> names = (List<String>) getVariable("names");
System.out.println("names = " + names);
Output:
names = [A, B, C, D]
note: I extracted method for readability, but you can see them below
But, again that's just different, not really more efficient than the regular for loop.
Here's the complete source code. To run it you just need Java1.6 and Groovy in the classpath.