Is it possible perform multiple mapping on collection?
Following code compilation error:
... in Stream cannot be applied to java.util.function.Function<capture<?>,capture<?>>
private static List<?> multipleMapping(final Collection<?> collection, final List<Function<?, ?>> functions) {
Stream<?> stream = collection.stream();
for (Function<?, ?> function : functions) {
stream = stream.map(function);
}
return stream.collect(Collectors.toList());
}
I would like to generic solution.
The problem comes from the fact that you're using a generic wildcard ?. What you want is to have a parameterized type T, that will represent the type of the Stream element. Assuming the function would return the same type as their input, you could have:
private static <T> List<T> multipleMapping(final Collection<T> collection, final List<Function<T, T>> functions) {
Stream<T> stream = collection.stream();
for (Function<T, T> function : functions) {
stream = stream.map(function);
}
return stream.collect(Collectors.toList());
}
This compiles fine: the mapper given to map correcly accepts a T and returns a T. However, if the functions don't return the same type as their input then you won't be able to keep type-safety and will have to resort to using List<Function<Object, Object>>.
Note that we could use a UnaryOperator<T> instead of Function<T, T>.
Also, you could avoid the for loop and reduce all functions into a single one using andThen:
private static <T> List<T> multipleMapping(final Collection<T> collection, final List<Function<T, T>> functions) {
return collection.stream()
.map(functions.stream().reduce(Function.identity(), Function::andThen))
.collect(Collectors.toList());
}
If you have few functions (i.e. if you can write them down), then I suggest you don't add them to a list. Instead, compose them into a single function, and then apply that single function to each element of the given collection.
Your multipleMapping() method would now receive a single function:
public static <T, R> List<R> multipleMapping(
Collection<T> collection, Function<T, R> function) {
return collection.stream()
.map(function)
.collect(Collectors.toList());
}
Then, in the calling code, you could create a function composed of many functions (you will have all the functions anyway) and invoke the multipleMapping() method with that function.
For example, suppose we have a list of candidates:
List<String> candidates = Arrays.asList(
"Hillary", "Donald",
"Bernie", "Ted", "John");
And four functions:
Function<String, Integer> f1 = String::length;
Function<Integer, Long> f2 = i -> i * 10_000L;
Function<Long, LocalDate> f3 = LocalDate::ofEpochDay;
Function<LocalDate, Integer> f4 = LocalDate::getYear;
These functions can be used to compose a new function, as follows:
Function<String, Integer> function = f1.andThen(f2).andThen(f3).andThen(f4);
Or also this way:
Function<String, Integer> composed = f4.compose(f3).compose(f2).compose(f1);
Now, you can invoke your multipleMapping() method with the list of candidates and the composed function:
List<Integer> scores = multipleMapping(candidates, function);
So we have transformed our list of candidates into a list of scores, by explicitly composing a new function from four different functions and applying this composed function to each candidate.
If you want to know who will win the election, you could check which candidate has the highest score, but I will let that as an exercise for whoever is interested in politics ;)
Related
I have this problem.
I am working with some generics and at one point I will need a specific converted depending on each type.
So, so far I have this:
public static <T> List<T> myMethod(List<T> list1, List2<T>, SomeFunction converter) {
//... do suff
return converter.convert(list1, list2);
}
and converter would be like this:
public <T> List<T> converter(List<T> list1, List<T> list2) {
/// cast and do stuff)
return List<T> some stuff;
}
Then I would like to make a call like
myMethod<list1,list2,converter);
I know about the functional internface Function but I need to send two parameters for this, is there any way I could do it in Java8/11?
Ideas?
Look into BinaryOperator which represents an operation upon two operands of the same type, producing a result of the same type as the operands.
public static <T> List<T> someMethodName(List<T> list1, List<T> list2,
BinaryOperator<List<T>> converter) {
return converter.apply(list1, list2);
}
BiFunction is also another option as it represents a function that accepts two arguments and produces a result.
public static <T, R> List<R> someMethodName(List<T> list1, List<T> list2,
BiFunction<List<T>, List<T>, List<R>> converter) {
return converter.apply(list1, list2);
}
To call the function let's assume you have two Integer lists for example sakes:
List<Integer> first = ....
List<Integer> second = ....
and you wanted to concatenate them, you'd pass both lists and a behaviour e.g.
List<Integer> concat = someMethodName(first, second,
(l, r) -> Stream.concat(l.stream(), r.stream())
.collect(Collectors.toList()));
You can make your function take a java.util.function.BinaryOperator<T>, which is a function that takes two parameters of the same type and returns a result of that same type:
public static <T> List<T> (List<T> list1, List<T>,
BinaryOperator<List<T>> converter) {
return converter.apply(list1, list2);
}
How can one idiomatically enumerate a Stream<T> which maps each T instance to a unique integer using Java 8 stream methods (e.g. for an array T[] values, creating a Map<T,Integer> where Map.get(values[i]) == i evaluates to true)?
Currently, I'm defining an anonymous class which increments an int field for use with the Collectors.toMap(..) method:
private static <T> Map<T, Integer> createIdMap(final Stream<T> values) {
return values.collect(Collectors.toMap(Function.identity(), new Function<T, Integer>() {
private int nextId = 0;
#Override
public Integer apply(final T t) {
return nextId++;
}
}));
}
However, is there not a more concise/elegant way of doing this using the Java 8 stream API? — bonus points if it can be safely parallelized.
Your approach will fail, if there is a duplicate element.
Besides that, your task requires mutable state, hence, can be solved with Mutable reduction. When we populate a map, we can simple use the map’s size to get an unused id.
The trickier part is the merge operation. The following operation simply repeats the assignments for the right map, which will handle potential duplicates.
private static <T> Map<T, Integer> createIdMap(Stream<T> values) {
return values.collect(HashMap::new, (m,t) -> m.putIfAbsent(t,m.size()),
(m1,m2) -> {
if(m1.isEmpty()) m1.putAll(m2);
else m2.keySet().forEach(t -> m1.putIfAbsent(t, m1.size()));
});
}
If we rely on unique elements, or insert an explicit distinct(), we can use
private static <T> Map<T, Integer> createIdMap(Stream<T> values) {
return values.distinct().collect(HashMap::new, (m,t) -> m.put(t,m.size()),
(m1,m2) -> { int leftSize=m1.size();
if(leftSize==0) m1.putAll(m2);
else m2.forEach((t,id) -> m1.put(t, leftSize+id));
});
}
I would do it in this way:
private static <T> Map<T, Integer> createIdMap2(final Stream<T> values) {
List<T> list = values.collect(Collectors.toList());
return IntStream.range(0, list.size()).boxed()
.collect(Collectors.toMap(list::get, Function.identity()));
}
For sake or parallelism, it can be changed to
return IntStream.range(0, list.size()).parallel().boxed().
(...)
Comparing to convert the input stream to List first in the solution provided by Andremoniy. I would prefer to do it in different way because we don't know the cost of "toList()" and "list.get(i)", and it's unnecessary to create an extra List, which could be small or bigger
private static <T> Map<T, Integer> createIdMap2(final Stream<T> values) {
final MutableInt idx = MutableInt.of(0); // Or: final AtomicInteger idx = new AtomicInteger(0);
return values.collect(Collectors.toMap(Function.identity(), e -> idx.getAndIncrement()));
}
Regardless to the question, I think it's a bad design to pass streams as parameters in a method.
This question already has answers here:
Java Lambda Stream Distinct() on arbitrary key? [duplicate]
(9 answers)
Closed 7 years ago.
Let's prefix this by my objects equals implementation is not how I need to filter so distinct itself does not work.
class MyObject {
String foo;
MyObject( String foo ) {
this.foo = foo;
}
public String getFoo() { return foo; }
}
Collection<MyObject> listA = Arrays.asList("a", "b", "c").stream().map(MyObject::new)
.collect(Collectors.toList());
Collection<MyObject> listB = Arrays.asList("b", "d").stream().map(MyObject::new)
.collect(Collectors.toList());
// magic
How can I merge and deduplicate the lists so that the resulting list should be of MyObjects containing "a", "b", "c", "d"?
Note: This is a simplification of what methods we actually need to deduplicate, which are actually complex DTOs of entities loaded by hibernate, but this example should adequately demonstrate the objective.
Such feature is discussed by JDK developers (see JDK-8072723) and might be included in Java-9 (though not guaranteed). The StreamEx library developed by me already has such feature, so you can use it:
List<MyObject> distinct = StreamEx.of(listA).append(listB)
.distinct(MyObject::getFoo).toList();
The StreamEx class is an enhanced Stream which is completely compatible with JDK Stream, but has many additional operations including distinct(Function) which allows you to specify key extractor for distinct operation. Internally it's pretty similar to the solution proposed by #fge.
You can also consider writing custom collector which will combine getting distinct objects and storing them to list:
public static <T> Collector<T, ?, List<T>> distinctBy(Function<? super T, ?> mapper) {
return Collector.<T, Map<Object, T>, List<T>> of(LinkedHashMap::new,
(map, t) -> map.putIfAbsent(mapper.apply(t), t), (m1, m2) -> {
for(Entry<Object, T> e : m2.entrySet()) {
m1.putIfAbsent(e.getKey(), e.getValue());
}
return m1;
}, map -> new ArrayList<>(map.values()));
}
This collector intermediately collects the results into Map<Key, Element> where Key is the extracted Key and Element is the corresponding stream element. To make sure that exactly first occurring element will be preserved among all repeating ones, the LinkedHashMap is used. Finally you just need to take the values() of this map and dump them into the list. So now you can write:
List<MyObject> distinct = Stream.concat(listA.stream(), listB.stream())
.collect(distinctBy(MyObject::getFoo));
If you don't care whether the resulting collection is list or not, you can even remove the new ArrayList<>() step (just using Map::values as a finisher). Also more simplifications are possible if you don't care about order:
public static <T> Collector<T, ?, Collection<T>> distinctBy(Function<? super T, ?> mapper) {
return Collector.<T, Map<Object, T>, Collection<T>> of(HashMap::new,
(map, t) -> map.put(mapper.apply(t), t),
(m1, m2) -> { m1.putAll(m2); return m1; },
Map::values);
}
Such collector (preserving the order and returning the List) is also available in StreamEx library.
If .equals() does not work for you then you may want to have a go at using Guava's Equivalence.
Provided that your type is T, you need to implement an Equivalence<T>; once you have this, you need to create a:
Set<Equivalence.Wrapper<T>>
into which you'll gather your values. Then, provided your implementation of Equivalence<T> is some static variable named EQ, adding to this set is as simple as:
coll1.stream().map(EQ::wrap).forEach(set::add);
coll2.stream().map(EQ::wrap).forEach(set::add);
And then to obtain a List<T> from this set, you could:
final Set<T> unwrapped = set.stream().map(Equivalence.Wrapper::get)
.collect(Collectors.toSet());
But of course, since in your comments you say you can do it with a loop, well... Why not keep using that loop?
If it works, don't fix it...
Collection<MyObject> result = Stream.concat(listA.stream(), listB.stream())
.filter(distinct(MyObject::getFoo))
.collect(Collectors.toList());
public static <T> Predicate<T> distinct(Function<? super T, Object> keyExtractor) {
Map<Object, String> seen = new ConcurrentHashMap<>();
return t -> seen.put(keyExtractor.apply(t), "") == null;
}
I found this distinct function once in a blog (can't remember the link atm).
I am trying to simplify down one of my methods, which uses some Java 8, but need not to use it:
public <T, U, R> Collection<R> singleCollectionOf(final Collection<T> collectionA, final Collection<U> collectionB, final Supplier<Collection<R>> supplier, final BiFunction<T, U, R> mapper) {
if (Objects.requireNonNull(collectionA).size() != Objects.requireNonNull(collectionB).size()) {
throw new IllegalArgumentException();
}
Objects.requireNonNull(supplier);
Objects.requireNonNull(mapper);
Iterator<T> iteratorA = collectionA.iterator();
Iterator<U> iteratorB = collectionB.iterator();
Collection<R> returnCollection = supplier.get();
while (iteratorA.hasNext() && iteratorB.hasNext()) {
returnCollection.add(mapper.apply(iteratorA.next(), iteratorB.next()));
}
return returnCollection;
}
Now you see here that I need to supply a Supplier<Collection<R>> to be able to create the resulting collection.
Is there a way to capture, if and only if, the collection type of collectionA is equal to that of collectionB the type of collection that has been used and reduce the need to provide the custom supplier?
I would like to be able to call it like this, in pseudo code:
singleCollectionOf(ArrayList<T>, ArrayList<U>, BiFunction<T, U, R>);
and then it would need to be able to supply the ArrayList::new itself as supplier.
Is this possible? Is there a way to figure out the type of the actual object, and not the reference type, in case both are ArrayList, but stored with a List reference?
Clarifying example:
List<T> list1 = new ArrayList<>();
List<U> list2 = new ArrayList<>();
singleCollectionOf(list1, list2, ...);
I am trying to sort and get the top 3 maxium values for the VALUE(?)
Map<String,?> points = map
Tried using Map<String, String> and it works fine, but in this case I need Map<String, ?>.
The type of the values will need to implement Comparable.
If the size of the map is not too large, you could try something like this:
private <T extends Comparable> SortedSet<T> sortValues(final Map<?, T> m)
{
final SortedSet<T> result = new TreeSet<>();
result.addAll(m.values());
return result;
}
I haven't tested this, but I think I have all of the type declarations correct. Then just take the first, or last as the case may be, elements from the sorted set. I believe you can optionally provide your own Comparator to the set to choose how to order the objects.
You cannot sort non-Comparable objects, to begin with, so you'll need a map with Comparable values, not ?. Having that, however, you could do something like this:
public static <T extends Comparable<? super T>> List<T> sortValues(Map<?, T> map) {
List<T> buffer = new ArrayList<T>(map.values());
Collections.sort(buffer);
return(buffer);
}
Then you can do whatever you want with the return value of sortValues, such as picking the first three objects in it.