JAVA generic class method not applicable - java

public class IdfMap<K, V> extends HashMap<K, V>{
public IdfMap() {
super();
}
public IdfMap(int initialCapacity){
super(initialCapacity);
}
public IdfMap(int initialCapacity, float loadFactor){
super(initialCapacity, loadFactor);
}
public <K, V extends Comparable<? super V>> SortedSet<Map.Entry<K, V>> entriesSortedByValues(){
SortedSet<Map.Entry<K, V>> sortedEntries = new TreeSet<>(
new Comparator<Map.Entry<K, V>>() {
#Override
public int compare(Map.Entry<K, V> e1, Map.Entry<K, V> e2){
return e2.getValue().compareTo(e1.getValue());
}
}
);
sortedEntries.addAll(this.entrySet());
return sortedEntries;
}
}
The line
sortedEntries.addAll(this.entrySet());
does not work. Why? It tells me that the method is not applicable for the given argument, which is a pretty vague error statement to understand. I would except this.entrySet() to return the set, which should in theory be usable for the addAll method.

Your method introduces its own generic type parameters, also called K and V, but completely different from the ones defined by the class.
As a result, within the method, K and V refer to something different from (hence incompatible with) the "real" types.
This is like local variables shadowing member variables. And since they have the same name, the error message becomes hard to understand.
Remove the type parameter declaration from the method, should be
public SortedSet<Map.Entry<K, V>> entriesSortedByValues(){

Read the error message. It says The method addAll(Collection<? extends Map.Entry<K,V>>) in the type Set<Map.Entry<K,V>> is not applicable for the arguments
(Set<Map.Entry<K,V>>)
This is because you are mixing Maps and Sets.

Related

Java Collection Utils using Java Stream API

I am trying to write my own CollectionUtils helper class that other application will use. Here is my first method
public static <T, K, V>
Map<K, List<V>> listToMap(List<T> inputList,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper)
{
Collector c = Collectors.toMap(keyMapper, valueMapper);
return inputList.stream()
.collect(c);
}
public void test()
{
// trying to get a Map<String, List<String>> showing student's name and their activities.
listToMap(StudentDatabase.getAllStudents(), Student::getName, Student::getActivites);
}
However, I am getting lots of compilation error that I do not understand how to solve. Can I get some help here?
Is there any third party library that already does that (but it has to be using java stream api) I can use so that I do not need to write my own?
I tried above and having compilation issue.
There's a couple of problems with the current code:
The Collector interface is generic. You should parameterize it like you're doing for all the other generic types in the code. See What is a raw type and why shouldn't we use it? for more information.
You've defined the return type as Map<K, List<V>>. That would seem to indicate you're trying to implement a grouping operation. However, there are three other parts of your code indicating otherwise:
You use Collectors#toMap(Function,Function)
Your valueMapper maps to V, not List<V>
You call listToMap with Student::getActivities as an argument for the valueMapper, and I can only assume that method returns a list of activities (or some other collection).
So, given all that, you should change the return type to Map<K, V>. That gives the caller full control over the value type of the map, rather than forcing them to use a list. But if you are trying to implement a grouping operation, and you always want the value type to be a List<V>, then consider using Collectors#groupingBy(Function,Collector) instead.
Fixing those two things will give you something like:
public static <T, K, V> Map<K, V> listToMap(
List<T> list,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper) {
Collector<T, ?, Map<K, V>> collector = Collectors.toMap(keyMapper, valueMapper);
return list.stream().collect(collector);
}
And here's a minimal example using the above:
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class Main {
public record Student(String name, List<String> activities) {}
public static <T, K, V> Map<K, V> listToMap(
List<T> list,
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends V> valueMapper) {
Collector<T, ?, Map<K, V>> collector = Collectors.toMap(keyMapper, valueMapper);
return list.stream().collect(collector);
}
public static void main(String[] args) {
List<Student> students = List.of(
new Student("John", List.of("Piano", "Running")),
new Student("Jane", List.of("Soccer", "Video Games")),
new Student("Bob", List.of("Snowboarding"))
);
Map<String, List<String>> map = listToMap(students, Student::name, Student::activities);
System.out.println(map);
}
}
Output:
{Bob=[Snowboarding], John=[Piano, Running], Jane=[Soccer, Video Games]}
The method should return a Map instead of a Collector. Also, the Collectors.toMap is not enough to convert a List to a Map of List. You need to use a groupingBy collector instead of toMap.
return inputList.stream().collect(Collectors.groupingBy(keyMapper, Collectors.mapping(valueMapper, Collectors.toList())));

Do Collections functional methods call get, put, etc?

I've spent many years working with Java 1.6 (maintaining legacy tools) and am just starting to migrate to 1.8. One big change is the functional methods in the java.util.Collections suite. The biggest concern for me is I have several collection extensions which apply careful checks or algorithms on modification. Do the default methods call the already defined put(..), get(...), remove(..) etc functions or do I have to do a major rework to make this work?
E.g. (ignoring null checks, etc. map that only holds values <= 10)
public class LimitedMap extends HashMap<String, Integer>{
#Override
public Integer put(String key, Integer value){
if(value> 10) throw new IllegalArgumentException();
return super.put(key, value);
}
#Override
public Integer computeIfAbsent(String key, Function<? super String, ? extends Integer> mappingFunction) {
return super.computeIfAbsent(key, mappingFunction);
}
}
With this pair of functions: would I still have to do a detailed override and put new checks into the computeIfAbsent function?
The only way you could be certain that only the interface methods from pre Java 8 can be used is if you somehow could delegate to the default method implementation in the interface (Map<K, V> in this case).
That is, if you could write something like the following (which you can't).
public class LimitedMap extends HashMap<String, Integer> {
#Override
public Integer computeIfAbsent(String key,
Function<? super String, ? extends Integer> mappingFunction) {
return Map.super.computeIfAbsent(key, mappingFunction);
}
}
Unfortunately that is not legal since you only can invoke the method that you've overriden (here the one from HashMap<String, Integer>) but not the one which the inherited method might have overridden (these are the normal rules for super method invocation).
So the only workaround I see for your situation is to create a copy of the interface default method implementation in a helper class like this:
public class Maps {
public static <K, V> V computeIfAbsent(Map<K, V> map,
K key, Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = map.get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
map.put(key, newValue);
return newValue;
}
}
return v;
}
}
This is the implementation from java.util.Map as a static method enhanced by an additional parameter map for the instance to operate on.
With such a helper class you could now write
public class LimitedMap extends HashMap<String, Integer> {
#Override
public Integer computeIfAbsent(String key,
Function<? super String, ? extends Integer> mappingFunction) {
return Maps.computeIfAbsent(this, key, mappingFunction);
}
}
That's not the prettiest solution but one that should work with a limited amount of effort.

How to generate a Map backed by a List

I have a data model that looks like this:
class CustomField {
String key;
String value;
}
From an API that I can get instances of List<CustomField>. The keys are unique in the list, which means that this collection really should be a Map<String, String>. Operating on this list is a pain, since every operation requires iteration to check for existing keys (CustomField doesn't implement equals either)
How can I create a Map<String, String> "view" backed by this list, so that I can operate on it using the Map interface?
I want a generic method like: <T, K, V> Map<K, V> createMapBackedByList(List<T> list, BiFunction<K, V, T> elementMapper, Function<T, K> keyMapper, Function<T, V> valueMapper) or similar.
It would use the functions to map between the list elements and map keys and values.
The important thing here is that I want changes to the map to be reflected in the underlying list, which is why the Streams API does not work here...
EDIT: I can't modify the API or the CustomField class.
Simple:
Write your own
public class ListBackedMap<K, V> implements Map<K, V> {
which takes some sort of List<Pair<K,V>> on creation; and "defers" to that. Of course, that requires that your CustomField class implements that Pair interface. (which you would probably need to invent, too)
( alternatively: your new class extends AbstractMap<K,V> to safe you most of the work ).
And now your methods simply return an instance of such a Map.
In other words: I am not aware of a built-in wrapper that meets your requirements. But implementing one yourself should be pretty straight forward.
Edit: given the fact that the OP can't change the CustomField class, a simple helper such as
interface <K, V> MapEntryAdapter {
K getKey();
V getValue();
}
would be required; together with a specific implementation that knows how to retrieve key/value from an instance of CustomField. In this case, the map would be backed by a List<MapEntryAdapter<K, V>> instead.
I ended up trying to implement it myself and basing it on an AbstractList. It was actually easier than I first though...
public class ListBackedMap<T, K, V> extends AbstractMap<K, V> {
private final List<T> list;
private final BiFunction<K, V, T> keyValueToElement;
private final Function<T, K> elementToKey;
private final Function<T, V> elementToValue;
public ListBackedMap(List<T> list, BiFunction<K, V, T> keyValueToElement, Function<T, K> elementToKey, Function<T, V> elementToValue) {
this.list = list;
this.keyValueToElement = keyValueToElement;
this.elementToKey = elementToKey;
this.elementToValue = elementToValue;
}
#Override
public Set<Entry<K, V>> entrySet() {
return list.stream()
.collect(toMap(elementToKey, elementToValue))
.entrySet();
}
#Override
public V put(K key, V value) {
V previousValue = remove(key);
list.add(keyValueToElement.apply(key, value));
return previousValue;
}
public List<T> getList() {
return list;
}
}
It's not very performant (or thread safe), but it seems to do the job well enough.
Example:
List<CustomField> list = getList();
ListBackedMap<CustomField, String, String> map = new ListBackedMap<>(
list,
(key, value) -> new CustomField(key, value),
CustomField::getKey,
CustomField::getValue);

Filter and translating functionality for a custom map with Java 8

I have a task where I need to implement some functionallity to abstract methods. The idea is to use Java 8, but I'm kinda new to programming with Java 8. The following is the abstract class that I need to implement:
public abstract class SortedMap<K extends Comparable<K>, V> implements Iterable<Pair<K, V>>
{
/**
* Returns a map where all values have been translated using the function
* <code>f</code>.
*/
public abstract <C> SortedMap<K, C> map(Function<? super V, ? extends C> f);
/**
* Returns a map containing only the keys that satisfies
* the predicate <code>p</code>.
*/
public abstract SortedMap<K, V> filter(Predicate<? super K> p);
// ...
}
What I've got so far (with Java 8) is:
public final class SortedMapImpl<K extends Comparable<K>, V> extends SortedMap<K,V>
{
private final Map<K, V> map;
private SortedMapImpl(Map<K, V> map)
{
this.map = new HashMap<K, V>(map);
}
#Override
public <C> SortedMap<K, C> map(Function<? super V, ? extends C> f)
{
// TODO Auto-generated method stub
return null;
}
#Override
public SortedMap<K, V> filter(Predicate<? super K> p)
{
final Map<K, V> filteredMap = map.entrySet()
.stream()
.filter(Predicate<? super Entry<K, V>> p)
.collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
return new SortedMapImpl<K, V>(filteredMap);
}
// ...
}
As you can see I've got no clue at the moment how to implement the map() method, and the filter method is at least partly wrong. Any help is greatly appreciated!
I'll help you implement the filter, and hopefully you should get the idea and implement the map by yourself.
You need to filter the entries of the original map, and only keep the ones for which the key satisifes the key predicate:
public SortedMap<K, V> filter(Predicate<? super K> predicate) {
Map<K, V> filteredMap =
map.entrySet()
.stream()
.filter(entry -> predicate.test(entry.getKey()))
.collect(Collectors.toMap(entry -> entry.getKey(),
entry -> entry.getValue()));
return new SortedMapImpl<K, V>(filteredMap);
}
I would not use the name SortedMap, though: your map is not a map, it's not sorted either, and SortedMap is already a standard collection name, which will make your class confusing and cumbersome to use.

Java infinitely recursive self referential types

I'm trying to create an implementation of Map that takes collections as keys.
What do you even call this conundrum?
What is the right way to do the class signature?
class SubClass <K extends Collection<E>, V> implements Map<K, V>
^^ Is improper syntax, but indicates what I want to do.
class SubClass <K extends Collection<K>, V> implements Map<Collection<K>, V>
^^ Results in a SubClass for which you can never declare the generic type. K is infinitely recursive. It also doesn't describe the type of behavior I'm looking for.
class SubClass <K , V> implements Map<K, V>
^^ Doesn't enforce the constraint that K needs to be a Collection
class SubClass <K extends Collection, V> implements Map<K, V>
^^ Doesn't allow us to know the generic types of the Collection
class SubClass <E, K extends Collection<E>, V> implements Map<K, V>
^^ Works, but is rather unwieldy
You'll need a type parameter for the Collection element type, potentially a type parameter for the actual Collection type if you need it, and a type parameter for the values.
class SubClass<E, K extends Collection<E>, V> implements Map<K, V> { ... }
If you don't need the specific Collection type, you can use
class SubClass<E, V> implements Map<Collection<E>, V> { ... }
Concerning the various comments on your question
public class Example {
public static void main(String[] args) throws Exception {
Whatever<Self> s = new Whatever<>();
}
}
class Self extends ArrayList<Self> {
}
class Whatever<E extends Collection<E>> {
}

Categories

Resources