What I want to do is remove all elements in a collection that are less than a specified object. This is what I have:
static void removeAllLessThan(Collection c, Object x) {
for(Object a : c) if(a.compareTo(x) < 0) c.remove(a);
}
This does not work because Object has no compareTo method. What I want to know is how should I compare between objects? Also what is the natural comparator? Thank you.
using Collection<Comparable> instead of Collection,and implement Comparable to all your items in the collection. And change your method like:
static void removeAllLessThan(Collection<Comparable> c, Comparable x) {
for (Iterator<Comparable> it = c.iterator(); it.hasNext();)
if (it.next().compareTo(x) < 0)
it.remove();
}
Start by using generics, and let the caller declare what type of object he wants to filter by:
static <T> void removeAllLessThan(Collection<T> collection, T t) {...}
This isn't enough, however. You need to use a Comparator<T> to provide the ordering.
static <T> void removeAllLessThan(Collection<T> collection,
Comparator<T> comparator, T t) {...}
Then, allow the user some flexibility when working with inheritance. He has to do the equivalent of t1 < t2, but the comparator can be of any supertype of T, and the collection can be of anything that extends T:
static <T> void removeAllLessThan(Collection<? extends T> collection,
Comparator<? super T> comparator, T t) {...}
Then, there is a mistake with the deletion process. The only mechanism that can delete from a collection while iterating over it is the Iterator. Anything else risks a ConcurrentModificationException.
static <T> void removeAllLessThan(Iterable<? extends T> iterable,
Comparator<? super T> comparator, T t) {
for (Iterator<? extends T> it = iterable.iterator(); it.hasNext();) {
if (comparator.compare(it.next(), t) < 0) {
it.remove();
}
}
}
Note the signature change.
Finally, this is a very specific method. You will need to write lots of nearly identical methods like removeIfGreaterThan, removeIfEqualIgnoringCase, etc. Write a generic removeIf method, with signature
public <T> removeIf(Iterable<? extends T> iterable,
Predicate<? super T> predicate){...}
Predicate is an interface provided by lots of libraries that has just a boolean eval method.
There are two ways to solve this problem.
First, you can use Comparable Interface, which means that the method should be changed into:
static void removeAllLessThan(Collection<Comparable> c, Comparable x) {
for(Comparable a : c) if(a.compareTo(x) < 0) c.remove(a);
}
An object should be comparable to have a method called comparedTo.
Second, you can implement a Comparator to judge which is bigger. If you don't want the object to be comparable, which means you don't want to change the existing code to let it implement Comparable, this is a better way. But the code will be change into:
static void removeAllLessThan(Collection c, Object x, Comparator comp) {
for(Object a : c) if(comp(a, x) < 0) c.remove(a);
}
Here is the javadoc of Comparable and Comparator.
Related
<? extends T> makes for a read-only collection
<? super T> makes for a write-only collection
I somehow get why use a read-only collection,for instance to use it in a multithreaded environment (any other cases?)
But why use a write-only collection? What's the point if you cannot read from it and use its values at some point? I know that you can get an Object out of it but that defies type safety.
Edit:
#Thomas the linked question (Difference between <? super T> and <? extends T> in Java) does show how to make a write only collection but does not answer 'why' would you need one in the first place.So it's not a duplicate
Statements like
<? extends T> makes for a read-only collection
<? super T> makes for a write-only collection
are just wrong. Wildcard element types do not say anything about the ability to read or write.
To show counter examples:
static <T> void modify(List<? extends T> l) {
l.sort(Comparator.comparing(Object::toString));
l.remove(l.size() - 1);
Collections.swap(l, 0, l.size() - 1);
l.add(null);
duplicateFirst(l);
}
static <U> void duplicateFirst(List<U> l) {
U u = l.get(0);
l.add(u);
}
shows quite some modifications possible for the List<? extends T>, without problems.
Likewise, you can read a List<? super T>.
static <T> void read(List<? super T> l) {
for(var t: l) System.out.println(t);
}
Usage restrictions imposed by ? extends T or ? super T are only in relation to T. You can not take an object of type T, e.g. from another method parameter, and add it to a List<? extends T>, because the list’s actual type might be a subtype of T. Likewise, you can not assume the elements of a List<? super T> to be of type T, because the list’s actual type might be a supertype of T, so the only assumption you can make, is that the elements are instances of Object, as every object is.
So when you have a method like
public static <T> void copy(List<? super T> dest, List<? extends T> src)
the method can not take elements from dest and add them to src (in a typesafe way), but only the other way round.
It’s important to emphasize that unlike other programming languages, Java has use site variance, so the relationship between the two list described above only applies to the copy method declaring this relationship. The lists passed to this method do not have to be “consumer of T” and “producer of T” throughout their entire lifetime.
So you can use the method like
List<Integer> first = List.of(0, 1, 2, 3, 7, 8, 9);
List<Number> second = new ArrayList<>(Collections.nCopies(7, null));
Collections.copy(second, first);
List<Object> third = new ArrayList<>(Collections.nCopies(11, " x "));
Collections.copy(third.subList(2, 9), second);
System.out.println(third);
Yes, copy was a real life example. Online demo
Note how the second list changes its role from consumer of Integer to producer of Object for the two copy invocations while its actual element type is Number.
Other examples for ? super T
Collections.fill(List<? super T> list, T obj)
Collections.addAll(Collection<? super T> c, T... elements)
To sum it up, in Java, rules like PECS are relevant for the declaration of methods, to determine the (typical) roles of the arguments within the method itself. This raises the flexibility for the caller, as it allows combining different invariant types, like the example of copying from a List<Integer> to a List<Number>.
But never assume that the generic types tell anything about the ability to read or write a collection.
Note that "write only collection" depends on the point of view.
Lets write a method that adds a bunch of numbers to a collection:
public static void addNumbers(List<? super Integer> target, int count) {
for (int i = 0; i < count; i++) {
target.add(i);
}
}
For this method the list target is a write only list: the method can only add numbers to it, it can not use the values that it added to the list.
On the other side there is the caller:
public static void caller() {
List<Number> myList = new ArrayList<>();
addNumbers(myList, 10);
double sum = 0;
for (Number n: myList) {
sum += n.doubleValue();
}
System.out.println(sum);
}
This method works with a specific list (myList) and therefore can read the values that addNumbers stuffed into it.
For this method the list is not a write only list, for this method it is an ordinary list.
How can I bind a Java Supplier to an existing instance of an Object? For example, if I want to write my own compareTo() method with this header:
public static int myCompareTo(Object o1, Object o2, Supplier<Comparable> supplier) {...}
I want be able to call it like:
myCompareTo("Hello", "Hello2", String::length);
where String (with the capital letter) is a class and no object. So how can I bind the instance o1 to the supplier?
Here's what you were searching for (I believe):
public static <T, U extends Comparable<U>> int compare(T o1, T o2, Function<T, U> mapper) {
return mapper.apply(o1).compareTo(mapper.apply(o2));
}
You can call that like so:
compare("str1", "str2", String::length); // 0
Thanks for your answers. Actually I figured it out now. I wanted to have the supplied object instances (o1 and o2) to execute the given method. I found out that Supplier was the wrong interface instead I had to use Function. Here you can see my working simplified example:
public static <T> int myCompareTo(T o1, T o2, Function<T, Comparable> getter) {
return getter.apply(o1).compareTo(getter.apply(o2));
}
The reason, the interface has to be Function and not Supplier is, that only Function is equivalent to a lambda expression taking an object and calls the referenced method on the object.
For example, if you define the method reference as:
Function<TypeOfInstance, ReturnTypeOfReferencedMethod> methodReference = TypeOfInstance::referencedMethod();
then the equivalent lambda expression being executed is:
(instance) -> instance.referencedMethod()
Additional Information:
Edit: I know I could have done the same by using Comparator, but this example is very simplified. In my application a Function of this kind is neccessary. I had to create a compareTo function that sorts an ArrayList by more than one attribute because the main sorting attribute may not be unique in the list. I want to share my code with you, because I think it can be a interesting insight for you.
public static <T> int ultimateCompare(T o1, T o2, Function<T, Comparable>... getters) {
for (Function<T, Comparable> getter : getters) {
int result = getter.apply(o1).compareTo(getter.apply(o2));
if (result != 0) return result;
}
return 0;
}
With this for example, you can sort a list of persons by last name and if two of them are identical, you can use the first name to sort. With this solution you can change sorting at runtime.
Actually a more correct way to define your method would be:
private static <T, U extends Comparable<? super U>> int myCompareTo(T left, T right, Function<T, U> fu) {
return Comparator.comparing(fu).compare(left, right);
}
You can use
Comparator.comparing(String::length);
to obtain a comparator instance which you can pass to the method.
I am trying to understand how Java Generic works. Here is the problems.
public static void main(String args[]){
List<Object> obj = Arrays.<Object>asList(23,"456",56.89);
List<Integer> intb = new ArrayList<>();
intb.add(234);
intb.add(345);
Collections.copy(obj,intb);
for(Object d : obj){
System.out.println(d);
}
}
This will work fine.But I implemented my own copy function like this
public static <T> void copy(List<? super T> des,List<? extends T> scr){
for(T f : scr){
des.add(f);
}
}
Then I used copy(obj,intb) instead of Collections.copy(obj,intb). Then I am getting errors. I don't understand why,But I know "? extends T" means any type that is a subtype of T and "? super T" means any type that is a supertype of T.
You cannot add anything to this fixed-length list:
List<Object> obj = Arrays.<Object>asList(23,"456",56.89);
You can only set new values to its elements (which is what Collections.copy() does). See also the Javadoc:
Returns a fixed-size list backed by the specified array.
What you probably intended to do was this:
List<Object> obj = new ArrayList<>(Arrays.asList(23,"456",56.89));
Well, a little bit late, but why your compiler complained about your code is not only because you used a fixed-size (immutable) List as a parameter in your copy method.
List<Object> obj = Arrays.<Object>asList(23, "456", 56.89);
Collections.copy(obj, intb);
It is also because of your (copy) method declaration.
public static <T> void copy(List<? super T> des,List<? extends T> scr) {
The declaration above has two lists with wildcards. When you use wildcard type <?>, you give message to the compiler that you are ignoring the type information. Whenever you pass arguments to a generic type, the java compiler tries to infer the type of the passed argument as well as the type of the generics and to justify the type safety. Now,(in your copy method) you are trying to use the add() method to insert an element in the list. Since des(your destination list) doesn't know the exact type of object it holds, it is risky to add elements to it. Generics was introduced in the language to ensure type safety in the first place. So you can not use methods like add that modifies the object. I hope the default implementation of the copy method in the java.util.Collections will help clarify it.
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
I know there already exists the same code block, but there was no explanation for what is the reason behind why we can't use methods like add on Lists with wildcards, and that is mainly to protect type safety.
In copy method
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD ||
(src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}
copy replace the value with index from index 0.
public static <T> void copy(List<? super T> des,List<? extends T> scr){
for(T f : scr){
des.add(f);
}
}
in this approach add into tail.
The following code does not compile in Eclipse. It says "The method putHelper(List,int,E) in the type Abc is not applicable for the arguments (List <.capture#8-of extends E>",int,E)"
private <E> void putHelper(List<E> list, int i, E value) {
list.set(i, value);
}
public <E> void put(List<? extends E> list, int toPos, E value) {
// list.set(toPos,value);
putHelper(list, toPos, value);
}
I don't understand why it is so?
Because the code below works fine.
public <E> void put(List<? extends E> list,int fromPos, int toPos) {
putHelper(list,fromPos,toPos);
}
private <E> void putHelper(List<E> list,int i, int j) {
list.set(j,list.get(i));
}
And I understand that here the helper method is able to capture the wildcard type, but why not in the earlier code?
EDIT: In the third case, if I change type parameter in the put method to List<.? super E> and when I try to call the put() method from another method which takes a list, Eclipse doesn't compile it. It says, "The method put(List<.? super E>,int,E) in the type Abc is not applicable for the arguments (List <.capture#6-of extends E>",int,E)"
public static <E> void insertAndProcess(List<? extends E> list) {
// Iterate through the list for some range of values i to j
E value = list.get(i);
//Process the element and put it back at some index
put(list, i+1, value);
//Repeat the same for few more elements
}
private static <E> void putHelper(List<E> list, int i, E value) {
list.set(i, value);
}
public static <E> void put(List<? super E> list, int toPos, E value) {
putHelper(list, toPos, value);
}
Here, how can insertAndProcess() call put() method and use it in its implementation, while the user can still be able to call both these methods with say ArrayList<.Integer>?
This is because of the Get and Put Principle also known by the acronym PECS which stands of Producer Extends, Consumer Super.
This is explained in this SO question: What is PECS (Producer Extends Consumer Super)?
But basically:
public <E> void put(List<? extends E> list, int toPos, E value) {
// list.set(toPos,value);
putHelper(list, toPos, value);
}
<? extends E> can't be used here because the List is being used as a consumer (it is taking elements) so it should use super instead of extends.
public <E> void put(List<? super E> list, int toPos, E value) {
// list.set(toPos,value);
putHelper(list, toPos, value);
}
EDIT
In your 2nd case, List is acting as a producer because it is producing elements via the call to get() so you can use extends.
However, in your 3rd example, you are both getting and putting into the same list so I don't think you can use wildcards at all.
This compiles:
public static <E> void insertAndProcess(List<E> list) {
// Iterate through the list for some range of values i to j
E value = list.get(i);
// Process the element and put it back at some index
putHelper(list, i+1, value);
// Repeat the same for few more elements
}
Note that since we don't need to use any wildcards anyway because we are getting and setting from the same list so the type E must be the same no matter what it is.
Generics are sometimes a bit difficult. Try to analyze it step by step.
Imagine - in your first example (that doesn't compile) - you call the put method with the type variable E replaced by Number. As you are constraining the input parameter list with List<? extends E> it could be a List<Integer> for example. The parameter value is of type E (remember: E is Number), so it could be a Double for example. And this must not be allowed because you would try to add a Double into a List<Integer>.
In the second example you only have a constraint on the input parameter list. No other parameter is dependent on E. Setting and getting on a list will always work because the elements in question must be of the same type.
A solution:
Always remind the acronym PECS. See Wildcard (Java). That means you should declare the put method as following (in your first example):
public <E> void put(List<? super E> list, int toPos, E value) {
putHelper(list, toPos, value);
}
List<E> will accept only E class objects where as List<? extends E> can accept any sub-class of E.
When you say a method can accept only List<E> passing List<? extends E> will confuse the compiler because it does not know which type of Object your list will actually contain.
Note that in java, this is illegal:
List<E> list = new ArrayList<? extends E>();
E.g. List<Number> list = new ArrayList<Integer>();
In the second example, your list expects any sub-class of E and what your passing is list of E objects so its allowed.
E.g. List<? extends Number> list = new ArrayList<Integer>();
I am working on an assignment that requires building a generic Java method that sorts a linked list or an array but I don't understand how that would be possible.
LinkedList sorting implementation should work with iterators to be efficient, because indexing and swapping elements without iterators would take linear time. Thus generic sorting of LinkedList and array (or ArrayList) doesn't make much sense.
If you would like to write a generic ArrayList and array sorting, for example, you could provide implementation with two lambdas, for getting and setting an element at some index:
private static interface Get<E> {
E get(int index);
}
private static interface Set<E> {
void set(int index, E e);
}
private static <E extends Comparable<? super E>> void sort(
Get<E> get, Set<E> set, int size) {
// ... sorting algorithm
}
public static <E extends Comparable<? super E>> void sort(E[] array) {
// more verbose syntax if you use Java 7, but it shouldn't affect performance
sort(i -> array[i], (i, e) -> { array[i] = e; }, array.length);
}
public static <E extends Comparable<? super E>, T extends List<E> & RandomAccess>
sort(T arrayList) {
sort(arrayList::get, arrayList::set, arrayList.size());
}
I didn't measure, but this imlementation shouldn't be much slower, if any, than old simple approach, because JIT is able to inline lambdas seamlessly.