I'm kind of new to generics in Java and I've faced such a problem: Let's say you have your own List implementation and you want to provide a mechanism to simultaneously convert all elements using some kind of mapping (functional interface) and collect them into a new list.
While the idea and use of functional interface (IMapper in my case) is straightforwad I can't quite think of what signature a function performing mapping should have?
Here's a little use case example and what I thought of as well. It does not work unfortunately and I guess the main problem is: How the second V param type should be passed in such case?
public interface IMapper<T,V> { V map(T v); }
public class MyList<T> extends ArrayList<T> {
public MyList<V> map(IMapper <T,V> mapper) {
MyList<V> list = new MyList<>();
for(T v : this) {
list.add(mapper.map(v));
}
return list;
}
}
// in main
MyList<Integer> list1 = new MyList<>();
// fill etc..
IMapper<Integer,String> m = (i) -> { return i.toString(); };
// "Change" list
MyList<String> list2 = list1.map(m);
PS:
I think that such thing is most probably already implemented in Java (stream() and what follows I guess?) however it suppose to be exercise for me. Any tip would be much appreciated :)
You can add the map result type to you function definition as following:
class MyList<T> extends ArrayList<T> {
public <V> MyList<V> map(IMapper<T, V> mapper) {
MyList<V> list = new MyList<>();
for (T v : this) {
list.add(mapper.map(v));
}
return list;
}
}
Example:
MyList<Integer> number = new MyList<>();
number.add(1);
number.add(2);
number.add(3);
number.map(v -> "#" + v).forEach(System.out::println);
And you can have the same result using Java8 streams as following:
List<Integer> numberStream = new ArrayList<>();
numberStream.add(1);
numberStream.add(2);
numberStream.add(3);
numberStream.stream().map(v -> "#" + v).forEach(System.out::println);
You can define the generic parameter as a type parameter on your map method. Like this:
public <V> MyList<V> map(IMapper <T,V> mapper) {
...
}
Type parameters can be defined in two ways, either on a class or on a method. If it's defined on a class, it can be used throughout the class. If it's defined on a method, it can only be used in that method.
In your case, the T parameter is defined on the class, while the V parameter can be defined on the method.
Related
I have a for loop which applies a Function for each element of a list. The list could be of type String or Long. Depending on a condition, I need to either iterate the String list or the Long list. Is there a way where I can do it without using if - else?
public static <T, S> T weirdFunction(Function<S, T> myFunction, Boolean convertToLong){
List<String> stringList = fetchFromSomewhere();
if(convertToLong){
// fetch list of longs from elsewhere
}
for (<String or Long> id : List<String> or List<Long>) { // choose list type based on some method param
myFunction.apply(id); // myFunction is of type Function
}
}
Having a function that returns another function, to be applied "recursively"1, makes this tricky.
Here is how it can be done:
#FunctionalInterface
interface ChainFunction<T> extends Function<T, ChainFunction<T>> {
// Nothing to add
}
static <T> ChainFunction<T> applyFunctionChain(List<T> list, ChainFunction<T> myFunction) {
for (T id : list) {
myFunction = myFunction.apply(id);
}
return myFunction;
}
The method can be called with String and with Long, and any other type for that matter.
1) "Recursive" is not the right word, but I don't know what it's called.
This presents one approach to call a different function per type without using an if statement. It requires one to build a map of different classes the list contains, each with a particular function. The containing class that houses the methods is SpecialList There is a caveat in this. Since the return value is cast to its actual type it can't be assigned in the loop unless it is returned as an Object. If the return type is not needed, then the return type could be set to void and the FunctionalInterface in the map could be a Consumer
Map<Class<?>, Function<Object, Object>> map = new HashMap<>();
map.put(String.class, SpecialList::stringFnc);
map.put(Long.class, SpecialList::longFnc);
List<Object> list = List.of("String1", 123L, "String2", 1234L,
29292L, "String4");
for (Object ob : list) {
map.get(ob.getClass()).apply(ob);
}
public static String stringFnc(Object str) {
String v = (String)str;
// now do something with v.
System.out.println("String : " + v);
return v;
}
public static Long longFnc(Object lng) {
long v = (Long)lng;
// now do something with v
System.out.println("Long : " + v);
return v;
}
prints
String : String1
Long : 123
String : String2
Long : 1234
Long : 29292
String : String4
I have configuration files which can be populated with enums and their respective values and will then be read by my program.
For example, a configuration file (yaml format) may look like this:
SomeEnumClass:
- VALUE_A_OF_SOME_ENUM
- VALUE_B_OF_SOME_ENUM
- ANOTHER_VALUE
AnotherEnumClass:
- VALUE_1
- VALUE_3
- VALUE_3
- VALUE_7
[etc...]
Unfortunately this leads to duplication in my code (java) like this:
if (enumNameString.equals("SomeEnumClass")) {
Collection<SomeEnumClass> values = new ArrayList<>;
for (String listEntry : yamlConfig.getStringList(enumNameString)) {
values.add(SomeEnumClass.valueOf(listEntry));
}
return values;
} else if (enumNameString.equals("AnotherEnumClass")) {
Collection<AnotherEnumClass> values = new ArrayList<>;
for (String listEntry : yamlConfig.getStringList(enumNameString)) {
values.add(AnotherEnumClass.valueOf(listEntry));
}
return values;
} else if ...
} else if ...
} else if ...
(please keep in mind that this example is pseudo code)
So of course i'm trying to get rid of the duplicate code. But how?
Is it possible to:
Get a class from a string? ("SomeEnumClass" -> SomeEnumClass.class)
Then check if that class is castable to Enum or something?
Access the enum's valueOf() method from that cast?
As usual, all things reflection are typically evil. However to get all the enum constants for a fully named class:
Class.forName(enumNameString).getEnumConstants()
<T extends Enum<T> Enum.valueOf(Class<T>,String) is great, but I don't know of a reasonable, obviously safe way to narrow a Class<?> to a Class<T extends Enum<T>> (Class.asSubclass will get you as far as Class<T extends Enum>).
Slightly better it to switch (or keep a Map) onto available constants:
Enum<?>[] values = switch (enumNameString) {
case "SomeEnumClass" -> SomeEnumClass .values();
case "AnotherEnumClass" -> AnotherEnumClass.values();
default -> throw new Error();
};
Enum<?> en = Arrays.stream(values)
.filter(e -> e.name() == listEntry).findFirst().get();
If building a Map derived from classes, it may be easier to use EnumSet.allOf(Class<?>) than MyEnum.values(), Enum.valueOf() or Class.getEnumConstants()
You can create a Map<String, Class<?>> which contains the mapping like this:
private static final Map<String, Class<Enum<?>>> MAP;
static {
Map<String, Class<Enum<?>>> map = new HashMap<>();
map.put(SomeEnumClass.class.getSimpleName(), SomeEnumClass.class);
// your other enum classes
MAP = Collections.unmodifiableMap(map);
}
And then you can make use of Enum.valueOf(Class<Enum>, String):
Class<Enum<?>> enumClass = MAP.get(enumNameString);
if (enumClass != null) {
Collection<Enum<?>> values = new ArrayList<>;
for (String listEntry : yamlConfig.getStringList(enumNameString)) {
values.add(Enum.valueOf(enumClass, listEntry));
}
return values;
}
i have many filters calling methods of the streamed object and using String methods that take 1 parameter on the result:
String s = "aComarisonString";
stream().filter( p -> p.thisReturnsAString().startsWith(s) )
stream().filter( p -> p.thisReturnsAString2().startsWith(s) )
stream().filter( p -> p.thisReturnsAString().endsWith(s) )
stream().filter( p -> p.thisReturnsAString().contains(s) )
is there a way to generify the filter so it looks/works something like
.filter( compare(thisReturnsAString,contains(s) )
.filter( compare(thisReturnsAString2,endsWith(s) )
There are several ways, but I agree with #shmosel. There will be only little to no improvement in readability.
One of the possible solutions:
<V, P> Predicate<? super P> compare(Function<P, V> valueFunction, Predicate<V> matchPredicate) {
return p -> matchPredicate.test(valueFunction.apply(p));
}
A call to that method would look like:
stream().filter(compare(P::thisReturnsAString, s -> s.endsWith(comparisonString)))
Where P is the type of your object. A slightly adapted variant, which might however lead to lots of overloaded methods:
<V, C, P> Predicate<? super P> compare(Function<P, V> valueFunction, BiPredicate<V, C> matchPredicate, C value) {
return p -> matchPredicate.test(valueFunction.apply(p), value);
}
A call to that method could look like this:
stream().filter(compare(P::thisReturnsAString, String::endsWith, comparisonString))
No real gain here. Just an other way to write, what you wrote with more boilerplate code ;-)
EDIT: added the suggestion from #shmosel regarding Predicate vs Function
filter function is an intermediary operation and it require predicate to apply to each element to determine if it should be included in the new stream.
You need to create another utility method compare() in your utility class.
ComparatorPredicate.java :
You can define the business rules to compare two strings in helper methods. An example ...
public class ComparatorPredicate {
public static String criteria = "aComarisonString";
public static Predicate<Data> startWith(){
return p -> p.getValue().startsWith(criteria);
}
public static Predicate<Data> endsWith(){
return p -> p.getValue().endsWith(criteria);
}
public static Predicate<Data> contains(){
return p -> p.getValue().contains(criteria);
}
}
Assuming, you have list of data strings, then you can do the following;
public class Data {
String value;
// getters and setters
}
Then you do not need to add lambda expression in each statement. All you need to call comparator methods in PredicateCompartor class, which make your code clean and less repetitive.
// data list
List<Data> dataList = new ArrayList<>();
// list of strings start with
List<Data> list1 = stringList.stream().filter(startWith()).collect(Collectors.toList());
// list of strings ends with
List<Data> list2 = stringList.stream().filter(endsWith()).collect(Collectors.toList());
// list of strings contains
List<Data> list3 = stringList.stream().filter(contains()).collect(Collectors.toList());
Note; these are just examples for demonstration
I have a HashSet<T> of objects that I want to insert into a Map<String,T> where the String key is a property of T. What is most efficient way to do this? I'm guessing there is a better way than what I have currently:
Map<String,T> myMap = new HashMap<String,T>();
HashSet<T> mySet = new LinkedHashSet<T>();
... Add some T's to mySet
for(T t : mySet){
myMap.put(t.getObjectKey(), t);
}
You can achieve that in Java 8 using lambda expressions :
Map<String, T> myMap = mySet.stream()
.collect(Collectors.toMap(x -> x.getObjectKey(),x -> x));
where T is the type of your objects and assuming that x.getObjectKey() returns a String.
Demo here.
The Guava library allows you to use a more functional-style way to express roughly what you're doing, but it's not going to be more efficient, and with the current state of the Java programming language it won't be particularly pretty either.
Until we get lambda expressions in Java 8, you're pretty much stuck doing exactly what you've done.
A bit late to answer, bu i will make an answer: If i required to have options something like this, i would design a custom HashMap for myself following OOP tradition: allowing any type of Collection of type Set and List.
The Object class which are going to be mapped as (key, value) pair entry in HashMap<K, V> should have a function getRelationalKey() with return type K. Hence we need to mark the object class as a type Relational for forcing the implementation. We will use the help of an interface:
interface Relational<T>
{
public T getRelationalKey();
}
The HashMap<K, V> should have a function putAllFromRCollection(Collection) to put all such Object of the collection to the Map. Hence, the function declaration with signature should be something like: putAllFromRCollection(Collection<? extends Relational<K>> collection. Hence, the custom HashMap: MyHashMap can have an implementation like:
class MyHashMap<K, V> extends HashMap<K, V>
{
public void putAllFromRCollection(Collection<? extends Relational<K>> collection)
{
for(Relational<K> relObj: collection)
{
put(relObj.getRelationalKey(), (V)relObj);
}
}
}
It is time for a test case: Lets declare a class:
class ATestClass implements Relational<String>
{
public String name;
public String address;
public ATestClass(String name, String address) {
this.name = name;
this.address = address;
}
#Override
public String getRelationalKey() {
return name;// or address or whatever should be regarded as a key
}
}
And add some object of this class to a HashSet:
HashSet<ATestClass>testSet = new HashSet<>();
testSet.add(new ATestClass("AName", "A-Adress"));
testSet.add(new ATestClass("BName", "B-Adress"));
testSet.add(new ATestClass("CName", "C-Adress"));
testSet.add(new ATestClass("DName", "D-Adress"));
MyHashMap<String, ATestClass>myMap = new MyHashMap<>();
myMap.putAllFromRCollection(testSet);
System.out.println(myMap);
Output:
{
CName=CollectionsTest.ATestClass#c21495,
BName=CollectionsTest.ATestClass#14b7453,
DName=CollectionsTest.ATestClass#1d5550d,
AName=CollectionsTest.ATestClass#1186fab
}
The advantages are:
we are now allowed to define required data type of the key to make the relation with the object
set a specific target field of Class as the Key matching type of the HashMap'S key
Allowing any type of Collection<E> instead of just a HashSet
In this implementation however, i have avoided to provide code for resizing the map with the size of the provided Collection for performance achievement. Please refer to the putAll(Map) function's source for details.
Since Java doesn't allow passing methods as parameters, what trick do you use to implement Python like list comprehension in Java ?
I have a list (ArrayList) of Strings. I need to transform each element by using a function so that I get another list. I have several functions which take a String as input and return another String as output. How do I make a generic method which can be given the list and the function as parameters so that I can get a list back with each element processed. It is not possible in the literal sense, but what trick should I use ?
The other option is to write a new function for each smaller String-processing function which simply loops over the entire list, which is kinda not so cool.
In Java 8 you can use method references:
List<String> list = ...;
list.replaceAll(String::toUpperCase);
Or, if you want to create a new list instance:
List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
Basically, you create a Function interface:
public interface Func<In, Out> {
public Out apply(In in);
}
and then pass in an anonymous subclass to your method.
Your method could either apply the function to each element in-place:
public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) {
ListIterator<T> itr = list.listIterator();
while (itr.hasNext()) {
T output = f.apply(itr.next());
itr.set(output);
}
}
// ...
List<String> myList = ...;
applyToListInPlace(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
or create a new List (basically creating a mapping from the input list to the output list):
public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) {
List<Out> out = new ArrayList<Out>(in.size());
for (In inObj : in) {
out.add(f.apply(inObj));
}
return out;
}
// ...
List<String> myList = ...;
List<String> lowerCased = map(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
Which one is preferable depends on your use case. If your list is extremely large, the in-place solution may be the only viable one; if you wish to apply many different functions to the same original list to make many derivative lists, you will want the map version.
The Google Collections library has lots of classes for working with collections and iterators at a much higher level than plain Java supports, and in a functional manner (filter, map, fold, etc.). It defines Function and Predicate interfaces and methods that use them to process collections so that you don't have to. It also has convenience functions that make dealing with Java generics less arduous.
I also use Hamcrest** for filtering collections.
The two libraries are easy to combine with adapter classes.
** Declaration of interest: I co-wrote Hamcrest
Apache Commons CollectionsUtil.transform(Collection, Transformer) is another option.
I'm building this project to write list comprehension in Java, now is a proof of concept in https://github.com/farolfo/list-comprehension-in-java
Examples
// { x | x E {1,2,3,4} ^ x is even }
// gives {2,4}
Predicate<Integer> even = x -> x % 2 == 0;
List<Integer> evens = new ListComprehension<Integer>()
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// evens = {2,4};
And if we want to transform the output expression in some way like
// { x * 2 | x E {1,2,3,4} ^ x is even }
// gives {4,8}
List<Integer> duplicated = new ListComprehension<Integer>()
.giveMeAll((Integer x) -> x * 2)
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// duplicated = {4,8}
You can use lambdas for the function, like so:
class Comprehension<T> {
/**
*in: List int
*func: Function to do to each entry
*/
public List<T> comp(List<T> in, Function<T, T> func) {
List<T> out = new ArrayList<T>();
for(T o: in) {
out.add(func.apply(o));
}
return out;
}
}
the usage:
List<String> stuff = new ArrayList<String>();
stuff.add("a");
stuff.add("b");
stuff.add("c");
stuff.add("d");
stuff.add("cheese");
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String>
a.equals("a")? "1":
(a.equals("b")? "2":
(a.equals("c")? "3":
(a.equals("d")? "4": a
)))
});
will return:
["1", "2", "3", "4", "cheese"]
import java.util.Arrays;
class Soft{
public static void main(String[] args){
int[] nums=range(9, 12);
System.out.println(Arrays.toString(nums));
}
static int[] range(int low, int high){
int[] a=new int[high-low];
for(int i=0,j=low;i<high-low;i++,j++){
a[i]=j;
}
return a;
}
}
Hope, that I help you :)