So I am currenty trying to implement a method which does some filtering on lists regardless of their actual type. Here is the actual method:
public static <T extends List<String>> T filterList(T list, Predicate <String> predicate) {
T newList = ???
list.forEach(s -> {
if (predicate.test(s)) newList.add(s);
});
return newList;
}
So the generic type T is basically the some implementation of List such as ArrayList or LinkedList and regardless of their actual implementation I want to do some filtering through a Predicate passed as parameter. The return type of the method is the same as the list which is passed as a parameter. But how is it possible to instanciate an empty List based on T (see line 2)?
To show you how the method is intended to be used i provided an example. The following example would filter an ArrayList based on the length of the containing Strings:
ArrayList<String> listOfNames = new ArrayList<>();
listOfNames.add("stackoverflowuser");
listOfNames.add("sitaguptana");
listOfNames.add("nyan cat");
listOfNames.add("pedro");
Predicate<String> lengthUnderTen = (string) -> string.length() < 10;
ArrayList <String> result = filterList(listOfNames,lengthUnderTen);
If I am understanding your question correctly, then I don't see why you need to use generics at all here.
The following function will accept any class that extends List as a parameter e.g. an ArrayList, LinkedList etc.:
public static List<String> filterList(List<String> list, Predicate<String> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}
Full example:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class Example {
public static void main(String[] args) {
ArrayList<String> example1 = new ArrayList<>();
example1.add("abc");
example1.add("ghe");
LinkedList<String> example2 = new LinkedList<>();
example2.add("foo");
example2.add("bar");
List<String> result1 = filterList(example1, s -> s.contains("a"));
List<String> result2 = filterList(example2, s -> s.contains("f"));
}
public static List<String> filterList(List<String> list, Predicate<String> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}
}
If you could modify your method as
public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {
return list.stream().filter(predicate).collect(Collectors.toList());
}
It looks clean because it works for any type of List, not only for List<String>. This method will be more generic.
Have the caller pass in a Supplier<T> as well.
public static <T extends List<String>> T filterList(T list, Predicate <String> predicate, Supplier<T> listCreator) {
T newList = listCreator.get();
list.forEach(s -> {
// ...
You have a base class that already allows you to cleanly program to interface. You could design this API method to return a List object, keeping the generic type at element level.
Please note that this pattern is already provided by the standard API (List/Collection + Stream), so you shouldn't need to recreate it. - see note at bottom.
If you do not have any constraint regarding the type of list returned by the method, then it's this implementation's choice which list type it returns (using array list below):
public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {
List<T> newList = new ArrayList<>(); //You can choose a different type here
list.forEach(s -> {
if (predicate.test(s)) newList.add(s);
});
return newList;
}
If you leave it to your caller to choose what type of list is created, then perhaps you should take a factory:
public static <U, T extends List<U>> T filterList(T list,
Predicate<U> predicate, Supplier<T> newListFactory) {
T newList = newListFactory.get(); //You can choose a different type here
list.forEach(s -> {
if (predicate.test(s))
newList.add(s);
});
return newList;
}
Note: this pattern is already provided by the collections API:
java.util.stream.Stream.filter(Predicate<? super T>)
This allows you to do exactly the same thing, except that the creation of the returned list (say, you ran collect(Collectors.toList()))
You could create the class by reflection by passing a List<String> as parameter.
In fact you don't need to specify any wildcard for your list.
public static List<String> filterList(List<String> list, Predicate<String> predicate) throws InstantiationException, IllegalAccessException {
List<String> newList = list.getClass()
.newInstance();
list.forEach(s -> {
if (predicate.test(s)) newList.add(s);
});
return newList;
}
But note that a cleaner way would be to use a stream to collect the new List if the implementation doesn't matter :
public static List<String> filterList(List<String> list, Predicate<String> predicate) throws InstantiationException, IllegalAccessException {
return list.stream()
.filter(predicate)
.collect(Collectors.toList());
}
Related
public class Test {
static List<Object> listA = new ArrayList<>();
public static void main(final String[] args) {
final List<TestClass> listB = new ArrayList<>();
listB.add(new TestClass());
// not working
setListA(listB);
// working
setListA(listB.stream().collect(Collectors.toList()));
System.out.println();
}
private static void setListA(final List<Object> list) {
listA = list;
}
}
why does it work with streams and does not work for the simple set?
For the first case, it fails because List<TestClass> is not a subtype of List<Object>.1
For the second case, we have the following method declarations:
interface Stream<T> {
// ...
<R, A> R collect(Collector<? super T, A, R> collector)
}
and:
class Collectors {
// ...
public static <T> Collector<T, ?, List<T>> toList()
}
This allows Java to infer the generic type parameters from the context.2 In this case List<Object> is inferred for R, and Object for T.
Thus your code is equivalent to this:
Collector<Object, ?, List<Object>> tmpCollector = Collectors.toList();
List<Object> tmpList = listB.stream().collect(tmpCollector);
setListA(tmpList);
1. See e.g. here.
2. See e.g. here or here.
This line
setListA(listB);
doesn't work because List in Java is invariant, meaning List<TestClass> doesn't extends List<Object> when TestClass extends Object. More details here
This line
setListA(listB.stream().collect(Collectors.toList()));
works because Java infer Object for Collector's generic type from this method signature setListA(final List<Object> list) and so you actually pass List<Object> there
the type parameters of Java Generic is invariance which means it can't be inherited as type parameters class hierarchy. The common parent of List<TestClass> and List<Object> is List<?>.
you can see detailed answer about java generic wildcard from kotlin & java. for example:
List<String> strings = new ArrayList<String>();
List<CharSequence> sequences = strings; // can't work
List<? extends CharSequence> parent1 = strings; // works fine
List<?> parent2 = strings; // works fine
// ^--- is equaivlent to List<? extends Object>
the streams approach is transform a List<TestClass> to List<Object>. if you want it works without transform a List to another List by stream. your methods signature should be as below, and the Collection#addAll also does it in java:
List<?> listA = new ArrayList<>();
private static void setListA(List<?> list) {
listA = list;
}
I need to merge all elements of a listB into another list listA.
If an element is already present (based on a custom equality-check) in listA I don't want to add it.
I don't want to use Set, and I don't want to override equals() and hashCode().
Reasons are, I don't want to prevent duplicates in listA per se, I only want to not merge from listB if there are already elements in listA which I consider being equal.
I don't want to override equals() and hashCode() since that would mean I need to make sure, my implementation of equals() for the elements hold in every circumstance. It might however be, that elements from listB are not fully initialized, i.e. they might miss an object id, where that might be present in elements of listA.
My current approach involves an interface and a Utility-Function:
public interface HasEqualityFunction<T> {
public boolean hasEqualData(T other);
}
public class AppleVariety implements HasEqualityFunction<AppleVariety> {
private String manufacturerName;
private String varietyName;
#Override
public boolean hasEqualData(AppleVariety other) {
return (this.manufacturerName.equals(other.getManufacturerName())
&& this.varietyName.equals(other.getVarietyName()));
}
// ... getter-Methods here
}
public class CollectionUtils {
public static <T extends HasEqualityFunction> void merge(
List<T> listA,
List<T> listB) {
if (listB.isEmpty()) {
return;
}
Predicate<T> exists
= (T x) -> {
return listA.stream().noneMatch(
x::hasEqualData);
};
listA.addAll(listB.stream()
.filter(exists)
.collect(Collectors.toList())
);
}
}
And then I'd use it like this:
...
List<AppleVariety> appleVarietiesFromOnePlace = ... init here with some elements
List<AppleVariety> appleVarietiesFromAnotherPlace = ... init here with some elements
CollectionUtils.merge(appleVarietiesFromOnePlace, appleVarietiesFromAnotherPlace);
...
to get my new list in listA with all elements merged from B.
Is this a good approach? Is there a better/easier way to accomplish the same?
You want something like this:
public static <T> void merge(List<T> listA, List<T> listB, BiPredicate<T, T> areEqual) {
listA.addAll(listB.stream()
.filter(t -> listA.stream().noneMatch(u -> areEqual.test(t, u)))
.collect(Collectors.toList())
);
}
You don't need a HasEqualityFunction interface. You can reuse BiPredicate to test whether the two objects are equal with regard to your logic.
This code filters only the elements in listB which are not contained in listA as per the predicate given. It does traverse listA as many times as there are elements in listB.
An alternative and better performant implementation would be to use a wrapper class that wraps your elements and has as equals method your predicate:
public static <T> void merge(List<T> listA, List<T> listB, BiPredicate<T, T> areEqual, ToIntFunction<T> hashFunction) {
class Wrapper {
final T wrapped;
Wrapper(T wrapped) {
this.wrapped = wrapped;
}
#Override
public boolean equals(Object obj) {
return areEqual.test(wrapped, ((Wrapper) obj).wrapped);
}
#Override
public int hashCode() {
return hashFunction.applyAsInt(wrapped);
}
}
Set<Wrapper> wrapSet = listA.stream().map(Wrapper::new).collect(Collectors.toSet());
listA.addAll(listB.stream()
.filter(t -> !wrapSet.contains(new Wrapper(t)))
.collect(Collectors.toList())
);
}
This first wraps every element inside a Wrapper object and collects them into a Set. Then, it filters the elements of listB that are not contained in this set. The equality test is done by delegating to the given predicate. The constraint is that we also need to give a hashFunction to properly implement hashCode.
Sample code would be:
List<String> listA = new ArrayList<>(Arrays.asList("foo", "bar", "test"));
List<String> listB = new ArrayList<>(Arrays.asList("toto", "foobar"));
CollectionUtils.merge(listA, listB, (s1, s2) -> s1.length() == s2.length(), String::length);
System.out.println(listA);
You can use a HashingStrategy based Set from Eclipse Collections
If you can use MutableList interface:
public static void merge(MutableList<AppleVariety> listA, MutableList<AppleVariety> listB)
{
MutableSet<AppleVariety> hashingStrategySet = HashingStrategySets.mutable.withAll(
HashingStrategies.fromFunctions(AppleVariety::getManufacturerName,
AppleVariety::getVarietyName),
listA);
listA.addAllIterable(listB.asLazy().reject(hashingStrategySet::contains));
}
If you can't change the type of listA and listB from List:
public static void merge(List<AppleVariety> listA, List<AppleVariety> listB)
{
MutableSet<AppleVariety> hashingStrategySet = HashingStrategySets.mutable.withAll(
HashingStrategies.fromFunctions(AppleVariety::getManufacturerName,
AppleVariety::getVarietyName),
listA);
listA.addAll(ListAdapter.adapt(listB).reject(hashingStrategySet::contains));
}
Note: I am a contributor to Eclipse Collections.
I have two collections of objects. The objects in these two collections are of different types, and there is a custom matcher to check that they refer to the same thing. Also, the collections are in the same order. Say, we can compare the entities in these collections by name, they are sorted by that name, and we have a custom matcher that returns true if the names are the same.
What I need is a matcher that would iterate over both of these collections item by item and compare those pairs using the existing custom matcher (i can also modify it).
Does anyone know how to do this?
Here's what i mean:
List lA =....;
List lB =....;
// what i have:
for (int i = 0; i < lA.size(); i++) {
assertThat(lA.get(i), matchesUsingMyCustomMatcher(lB.get(i));
}
// what i would like to have
assertThat(lA, someMagicMatcher(myModifiedCustomMatcher(lB)));
Let's consider this test:
#Test
public void test() throws Exception {
List<MyType1> expected = ...;
List<MyType2> actual = ...;
assertThat(actual, containsUsingCustomMatcher(expected));
}
You can use the following helper function (matchesUsingMyCustomMatcher is the same function as in your example).
private Matcher<Iterable<? extends MyType2>> containsUsingCustomMatcher(List<MyType1> items) {
List<Matcher<? super MyType2>> matchers = new ArrayList<Matcher<? super MyType2>>();
for (MyType1 item : items) {
matchers.add(matchesUsingMyCustomMatcher(item));
}
return Matchers.contains(matchers);
}
Generic approach
For a more reusable approach maybe the following can help you. First we need a generic helper function.
public static <T, R> Matcher<Iterable<? extends R>> containsUsingCustomMatcher(List<T> items, MatcherFunction<T, R> matcherFunction) {
return Matchers.contains(createMatchers(items, matcherFunction));
}
private static <R, T> List<Matcher<? super R>> createMatchers(List<T> items, MatcherFunction<T, R> matcherFunction) {
List<Matcher<? super R>> matchers = new ArrayList<Matcher<? super R>>();
for (T item : items) {
matchers.add(matcherFunction.apply(item));
}
return matchers;
}
and an interface MatcherFunction. The implementation of this interface has to create the appropriate matchers
public interface MatcherFunction<T, R> {
Matcher<R> apply(T t);
}
Example usage to achieve the same as in the first part of this answer:
assertThat(actual, containsUsingCustomMatcher(expected, myCustomMatcherFunction()));
with
private MatcherFunction<MyType1, MyType2> myCustomMatcherFunction() {
return new MatcherFunction<MyType1, MyType2>() {
#Override
public Matcher<MyType2> apply(MyType1 t) {
return matchesUsingMyCustomMatcher(t);
}
};
}
Java 8 note:
These anonymous classes look kind of ugly. With Java 8 you can simply write
assertThat(actual, containsUsingCustomMatcher(expected, this::matchesUsingMyCustomMatcher));
Do you think it is possible to create something similar to this?
private ArrayList increaseSizeArray(ArrayList array_test, GenericClass) {
array_test.add(new GenericObject()); // instance of GenericClass
return array_test;
}
Yes, you can.
private static <T> List<T> pushBack(List<T> list, Class<T> typeKey) throws Exception {
list.add(typeKey.getConstructor().newInstance());
return list;
}
Usage example:
List<String> strings = new ArrayList<String>();
pushBack(strings, String.class);
Old question but I would imagine this is the preferred way of doing it in java8+
public <T> ArrayList<T> dynamicAdd(ArrayList<T> list, Supplier<T> supplier) {
list.add(supplier.get());
return list;
}
and it could be used like this:
AtomicInteger counter = ...;
ArrayList<Integer> list = ...;
dynamicAdd(list, counter::incrementAndGet);
this will add a number to the list, getting the value from AtomicInteger's incrementAndGet method
Also possible to use constructors as method references like this: MyType::new
simple solution!
private <GenericType> ArrayList increaseSizeArray(ArrayList array_test, GenericType genericObject)
{
array_test.add(new genericObject());
return ArrayList;
}
This code is simplified as much as I can from a more complex class structure. In the real code, there were sub-types of the Integer and Double types I use here.
I'm trying to use Java Generics with a type parameter. If the user requests the type of Number.class, we want to combine the List<Integer> list and the List<Double> list into a single list.
While the code works, I cannot get ride of the unchecked cast warning (see the TODO tag). The warning is:
Type safety: Unchecked cast from List<Integer> to Collection<? extends T>
But, if I remove the cast, I get a compile error:
The method addAll(Collection<? extends T>) in the type List<T> is not applicable for the arguments (List<Integer>).
My code:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class Generics1 {
static final List<Integer> intList = new ArrayList<Integer>(Arrays.asList(
1, 2, 3, 4));
static final List<Double> dblList = new ArrayList<Double>(Arrays.asList(
1.1, 2.2, 3.3));
public static <T extends Number> List<T> getObjects(Class<T> type) {
List<T> outList = new ArrayList<T>();
if (type == Number.class) {
// user asked for everything
// TODO: unchecked cast warnings here should be fixed
outList.addAll((Collection<? extends T>) intList);
outList.addAll((Collection<? extends T>) dblList);
} else {
// user asked for subtype of number
if (Integer.class.isAssignableFrom(type)) for (Integer i : intList)
if (type.isInstance(i)) {
T obj = type.cast(i);
outList.add(obj);
}
if (Double.class.isAssignableFrom(type)) for (Double d : dblList)
if (type.isInstance(d)) {
T obj = type.cast(d);
outList.add(obj);
}
}
return outList;
}
public static void main(String[] args) {
System.out.println("HI!");
System.out.println("integers: " + getObjects(Integer.class));
System.out.println("doubles: " + getObjects(Double.class));
System.out.println("numbers: " + getObjects(Number.class));
}
}
You could add this to your code:
#SuppressWarnings("unchecked")
Here is another SO post that explains "what" that means: What is SuppressWarnings ("unchecked") in Java?
And here is another one dealing with conversion of a link that may be useful: How do I fix "The expression of type List needs unchecked conversion...'?
Though with some recoding you could probably make the warning go away completely and not need to be suppressed.
(Class<T> type)
List<T> outList = new ArrayList<T>();
if (type == Number.class) {
// obviously, T==Number here, though the compiler doesn't know that
// so we do the cast. compiler will still warn. since the cast makes
// perfect sense and is obviously correct, we are ok with it.
List<Number> numList = (List<Number>)outList;
numList.addAll( intList);
numList.addAll( dblList);
} else {
The better solution, simply
for list in lists
for item in list
if item instance of type
add item to result
(previous answer deleted)
Here's a way of doing this with Guava:
#SuppressWarnings("unchecked")
public static <T> List<T> filterAndCollapse(final Class<T> type,
Collection<?> a, Collection<?> b) {
List combined = new ArrayList();
Predicate<Object> filter = new Predicate<Object>() {
public boolean apply(Object obj) {
return type.isInstance(obj);
}
};
combined.addAll(Collections2.filter(a, filter));
combined.addAll(Collections2.filter(b, filter));
return combined;
}
// ...
filter(Number.class, intList, dblList);
Edit: The fully-type safe way for comparison.
public static <T> List<T> filterAndCollapse(final Class<T> type,
Collection<?> a, Collection<?> b) {
List<T> combined = new ArrayList<T>();
Predicate<Object> filter = new Predicate<Object>() {
public boolean apply(Object obj) {
return type.isInstance(obj);
}
};
Function<Object, T> transform = new Function<Object, T>() {
public T apply(Object obj) {
return type.cast(obj);
}
};
combined.addAll(Collections2.transform(Collections2.filter(a, filter),
transform));
combined.addAll(Collections2.transform(Collections2.filter(b, filter),
transform));
return combined;
}
Unfortunately there's no way to filter and transform in one step with Guava, to my knowledge.