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.
Related
I need an immutable list where I can get derive a second immutable list preserving all elements of the previous list plus an additional element in Java (without additional libraries).
Note: This question is similar to What is an efficient and elegant way to add a single element to an immutable set? but I need a list and don't have Guava.
What I have tried so far:
var list = List.of(someArrayOfInitialElements);
var newList = Stream.concat(list.stream(), Stream.of(elementToAppend))
.collect(CollectorsCollectors.toUnmodifiableList());
That would work but creating a stream and copying elements one by one seems inefficient to me. You could basically bulk copy memory given that List.of() stores data in a field-based or array-based data structure.
Is there a more efficient solution than using streams? A better data structure in the Java standard library that I am missing?
I would create a new ArrayList append the element and then return that as an unmodifiable list. Something like,
private static <T> List<T> appendOne(List<T> al, T t) {
List<T> bl = new ArrayList<>(al);
bl.add(t);
return Collections.unmodifiableList(bl);
}
And to test it
public static void main(String[] args) {
List<String> al = appendOne(new ArrayList<>(), "1");
List<String> bl = appendOne(al, "2");
System.out.println(bl);
}
I get (unsurprisingly):
[1, 2]
See this code run at IdeOne.com.
The Answer by Frisch is correct, and should be accepted. One further note…
Calling Collections.unmodifiableList produces a collection that is a view onto the original mutable list. So a modification to the original list will "bleed through" to the not-so-immutable second list.
This issue does not apply to the correct code shown in that Answer, because the new ArrayList object deliberately goes out-of-scope. Therefore that new list cannot be accessed for modification. But in other coding scenarios, this issue could be a concern.
List.copyOf
If you want an independent and truly immutable second list, use List.copyOf in Java 10+. This returns an unmodifiable list.
return List.copyOf( bl ) ;
Both answers are great, I would create a bit more generic solution:
private static <T> List<T> append(final List<T> al, final T... ts) {
final List<T> bl = new ArrayList<>(al);
for (final T t : ts) {
bl.add(t);
}
return List.copyOf(bl);
}
It can be used exactly like previous answer:
List<String> al = append(new ArrayList<>(), "1");
List<String> bl = append(al, "2");
System.out.println(bl);
But also slightly more efficient:
List<String> bl = append(new ArrayList<>(), "1", "2");
System.out.println(bl);
A java class contains an instance variable which is a List.
I need to set the type of object that this List holds in a String variable.
private List<VARIABLE> myList;
String VARIABLE = "BasicDegreeClass";
Is this allowed to do in JAVA If so, how can this be achieved?
Thanks in advance
First, you will have to create a List where T is the Type of objects contained in the list.
So, your code should read as
private List<T> myList;
From the javadoc,
<T> the type of elements in this list.
In your case, the compiler will not know the type of elements that is to be added to your list and hence it won't work.
However you can use List<Object> as an alternate, but that is a generally code-smell in long run and very difficult to maintain
It kills the idea of generics.
Makes your code prone to ClassCastException.
In a perfect world and even if you are safe while adding elements,
you will need to suppress warning everywhere and need to cast back to
the type.
Proper Solution : You can write an interface, which all your objects of the ArrayList will adhere to. Proceed with something like
List<'YOUR INTERFACE TYPE HERE'> myList = new ArrayList<>();
Don't you need to worry of the object type inside the list. Hope this helps!
If you are too specific to save in String, here is a workaround.
public static <T> List<T> getCastedList(List<Object> objList, Class<T> clazz) {
List<T> newList = new ArrayList<T>();
if (objList != null) {
for (Object object : objList) {
if (object != null && clazz.isAssignableFrom(object.getClass())) {
newList.add((T) object);
}
}
}
return newList;
}
And call this as
getCastedList(myList, Class.forName(VARIABLE));
It would be better to use generics like this
private List<T> myList;
documentation here
but it is possible to use a string to represent a class name or act as a unique identifier, given you support this yourself
You can create List only with class not primitive types or its values.
Example:-
List<Integer> a = new ArrayList<>();
List<YourObject> obj = new ArrayList<>();
But if your will try with primitive types then you will get.
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() );
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 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.