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() );
Related
If I have a data structure which looks like this:
ImmutableObj<MutableMap<String,Integer>> myObj;
Is the above enough to ensure immunity? Or do I have to make MutableMap immutable as well?
It's safe when you don't expose link to original mutable object like this (example with lists, same method for map is presents in Collections class):
List<String> strings = Arrays.asList("Foo", "Bar");
List<String> immutableStrings = Collections.unmodifiableList(strings);
because you may have to modify immutableStrings list through strings list.
Better approach is to don't have links to mutable list at all like:
List<String> immutableStrings = Collections.unmodifiableList(Arrays.asList("Foo", "Bar"));
Here we don't have link to inner mutable list with foo and bar, and not able to modify immutableStrings list.
It's synthetic example, from conversation under your question i understand that you may want something like this:
public Map<String, Integer> wordFruquencyIn(String book, String word) {
//do calculation here
Map<String, Integer> result = //result
return Collections.unmodifiableMap(result); // return unmodifiable map
}
Better to create custom class that represents result of calculation instead of map, because contracts of Map, List, etc... unclear in way you don't know if they mutable or not (lack of method isMutable() and lack of design in early java versions in general (see LSP principle in this case)). After refactor you code may be like that:
public Statistics wordFruquencyIn(String book, String word) {
//do calculation here
Map<String, Integer> result = //result
return new StatisticsImpl(result);
}
public interface Statistics {
public String word() {}
public int times() {}
}
You may create any implementation of Statistics interface that fulfill contract of this interface and have map inside or any mutable data, only one way to communicate with this class is ask about word() that returns already immutable string or times() that returns a primitive.
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)
I have a list of objects that contains two string properties.
public class A {
public String a;
public String b;
}
I want to retrieve two Sets one containing property a and one b.
The naive approach is something long these lines:
List<A> list = ....
Set<String> listofa = new HashSet<>();
Set<String> listofb = new HashSet<>();
for (A item : list) {
if (item.a != null)
listofa.add(item.a);
if (item.b != null)
listofb.add(item.b);
}
Trying to do in a functional way in guava I ended up with this approach:
Function<String,A> getAFromList = new Function<>() {
#Nullable
#Override
public String apply(#Nullable A input) {
return input.a;
}
};
Function<String,A> getBFromList = Function<>() {
#Nullable
#Override
public String apply(#Nullable A input) {
return input.b;
}
};
FluentIterable<A> iterables = FluentIterable.from(list);
Set<String> listofAs = ImmutableSet.copyOf(iterables.transform(getAFromList).filter(Predicates.notNull()));
Set<String> listofBs = ImmutableSet.copyOf(iterables.transform(getBFromList).filter(Predicates.notNull()));
However this way I would iterate twice over the list.
Is there any way how to avoid iterating twice or multiple times ?
In general how does one solve these uses cases in a functional way in general (not only in guava/java) ?
Firstly you're after an optimisation - but if performance is key, use regular java methods over guava (i.e. your first method). See here.
I think because you want two results, at some point you will need to iterate twice (unless you pass in one of the sets to be populated but that that is definitely not a fp approach as it would not be a pure function).
However if iteration was expensive enough to need an optimisation you would iterate that once to an intermediate structure:
a_b_pairs = transformToJustAB(input) //single expensive iteration
list_of_a = transformA(a_b_pairs) //multiple cheaper iterations
list_of_b = transformB(a_b_pairs)
So the simple answer is that you have to iterate twice. Think about it. If you have N elements in your List you will need to do N inserts into the first Set and N inserts into the second Set. Functional or otherwise, you will have to iterate N twice whether it be on conversion (extraction) or insert.
If you were going for two Lists it would be different because you could create views and only iterate as needed.
This can be solved in one iteration with Multimaps.index:
Function<A, String> filterAB = new Function<A, String>() {
#Override
public String apply(A input) {
if (input.a != null) {
return "a";
}
if (input.b != null) {
return "b";
}
return "empty";
}
};
ImmutableListMultimap<String, A> partitionedMap = Multimaps.index(list, filterAB);
The output will be a Guava Multimap with three separate entries for:
an immutable list with all "a-not-null" objects under key "a".
an immutable list with all "b-not-null" objects under key "b".
and possibly an immutable list with objects where both a and b is null under key "empty".
What you're trying to achieve is partitioning or splitting of the collection using predicates.
With Guava, you can use Multimap.index. See related question and answer here.
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.