Java 8 Lambda Expressions - java

I've been stuck with one lambda expression and the Comparator class using Comparator.comparing(...).thenComparing(...) methods to sum up two way of sorting a Stream.
Both of my methods are working, but when I put them together nothing is working at all.
Here is the link if you want to try and validate the exercise:
http://codecheck.it/codecheck/files?repo=heigvdcs1&problem=poo3e
And here is what you have to do:
For each word in a stream, determine the “vowelness”, i.e. the number of vowels - the number of consonants. Produce the n words with the highest vowelness paired with the vowelness value. Sort first by vowelness, then by the string. Complete this program.
This time, you have a hidden static method long Words.vowels(String w) at your disposal that yields the number of vowels in w, including duplicates.
For now i have managed to do this:
import java.util.*;
import java.util.stream.*;
public class Streams
{
List<Pair<String, Long>> wordsWithManyVowels(Stream<String> words, int n)
{
return words
.map( w -> Pair.of( w , ( Words.vowels(w) - ( w.length() - Words.vowels(w)))))
.sorted(Comparator.comparingLong(f1 -> -f1.second())
//This part is working without the first comparing
//.thenComparing(f2 -> f2.first().length()))
.limit(n)
.collect(Collectors.toList());
}
}
The Pair class:
import java.util.Objects;
public class Pair<T, S>
{
private T first;
private S second;
public Pair(T firstElement, S secondElement)
{
first = firstElement;
second = secondElement;
}
/*
Use Pair.of(x, y) instead of new Pair<...,...>(x, y)
so you get the type inferred
*/
public static <T, S> Pair<T, S> of(T firstElement, S secondElement)
{
return new Pair<T, S>(firstElement, secondElement);
}
public T first() { return first; }
public S second() { return second; }
public String toString() { return "(" + first + "," + second + ")"; }
public boolean equals(Object otherObject)
{
if (this == otherObject)
return true;
if (otherObject == null || !(otherObject instanceof Pair))
return false;
#SuppressWarnings("unchecked") Pair<T, S> other = (Pair<T, S>) otherObject;
return Objects.equals(first, other.first) &&
Objects.equals(second, other.second);
}
}

Solution 1
Extracting comparing methods as static works like this
public class Streams {
List<Pair<String, Long>> wordsWithManyVowels(Stream<String> words, int n) {
return words
.map(w -> Pair.of(w, (Words.vowels(w) - (w.length() - Words.vowels(w)))))
.sorted(Comparator.comparingLong(Streams::vowelness).thenComparingInt(Streams::length))
.limit(n)
.collect(Collectors.toList());
}
static int length(Pair<String, Long> p) {
return p.first().length();
}
static long vowelness(Pair<String, Long> p) {
return -p.second();
}
}
Solution 2
Use this Comparator implementation without static methods
Comparator
.comparingLong((Pair<String, Long> p) -> -p.second())
.thenComparingInt((Pair<String, Long> p) -> p.first().length())
Note: see how thenComparingInt is used at the end of the composite comparator in the both solutions.
Solution 3
The problem of the source code is static Comparator methods need info about processing elements' type in a chain. By default Object type is used. So, it's possible to specify it this way (simpler than in Solution 2):
Comparator.<Pair<String, Long>>comparingLong(p -> -p.second()).thenComparing(p -> p.first().length())

Related

ArrayLists of various class types - generic method to find position of a key value in array

I have an Android app with multiple input forms with multiple drop downs.
For these forms, the user can enter and exit the record multiple times before they commit the record.
Hence if they chose something on a drop down, exited the record, came back in again - they would expect to see their last entry already preselected in the drop down.
Here is an example of one of the many class types that drives a drop down list:
public class SART implements Serializable
{
private String Code;
private String Description;
public String getCode() {return Code;}
public void setCode(String Code) {this.Code = Code;}
public String getDescription() {return Description;}
public void setDescription(String Description) {this.Description = Description;}
}
So I take a known value and look up its position in the array list that drives the drop down and select
this line in the drop down.
Here is an example of how I do it:
int FindApplicationMethodPosition(ArrayList<SART> applicationMethods,String strExistingId)
{
int intSARTPosition = -1;
if (strExistingId !=null)
{
for(int i = 0; i <applicationMethods.size(); i++){
if(applicationMethods.get(i).getCode().equals(strExistingId))
{
intSARTPosition = i;
break;
}
}
}
return intSARTPosition;
}
I have about 30 different versions of this peppered through my code and I would like to try to call just one generic version.
int FindPositionGeneric(Object array, String strExistingId)
{
int intRC = -1;
intRC = IntStream.range(0, array.size())
.filter(i -> array.get(i).getCode().equals(strExistingId))
.findFirst()
.orElse(-1);
return intRC;
}
But of course the compiler does not like this at all.
Any suggestions please ?
But of course the compiler does not like this at all.
It doesn't know how to get the elements from array, nor how to get the code from an element in the array.
Pass in a List<T>, and a Function<T, String> to allow it to extract the code:
<T> int FindPositionGeneric(List<T> array, Function<T, String> codeFn, String strExistingId)
{
int intRC = -1;
intRC = IntStream.range(0, array.size())
.filter(i -> codeFn.apply(array.get(i)).equals(strExistingId))
.findFirst()
.orElse(-1);
return intRC;
}
Note that this is inefficient for non-RandomAccess lists (e.g. LinkedList). An alternative solution would be a ListIterator-based loop:
ListIterator<T> it = array.listIterator();
while (it.hasNext()) {
int idx = it.nextIndex();
String code = codeFn.apply(it.next());
if (code.equals(strExistingId)) {
return idx;
}
}
return -1;
If all the 30 versions of the class have the same getCode(), then create an interface (let it be MyInterface) and let all the classes implement this interface. Then update the method as follows:
int FindPositionGeneric(List<MyInterface> list, String strExistingId)
{
int intRC = IntStream.range(0, list.size())
.filter(i -> list.get(i).getCode().equals(strExistingId))
.findFirst()
.orElse(-1);
return intRC;
}
Also, since you have the orElse, no need to initialize intRC separately.

Custom Comparator sort with multiple fields

Because this question is related to my last one, I will link it here.
Suppose I have a class TestB with two integers. I would be able to sort List<TestB> list on a and then on b like this:
list.sort(Comparator.comparing(TestB::getA).thenComparing(TestB::getB));
Now I want to know how to do that with the custom comparator in the last answer.
The custom Comparator version of list.sort(Comparator.comparing(TestB::getA).thenComparing(TestB::getB)); is:
list.sort(new Comparator<>() {
#Override
public int compare(TestB b1, TestB b2) {
int cmp = b1.getA().compareTo(b2.getA());
if (cmp == 0)
cmp = b1.getB().compareTo(b2.getB());
return cmp;
}
});
One option is to use what I call a custom generic multi-comparator:
list2.sort(getComparator( p -> p.getTestB().getA(),
p -> p.getTestB().getB() ));
private <T> Comparator<T> getComparator( Function<T, ? extends Comparable<?>>... functions ) {
return new Comparator<T>() {
#Override
public int compare(T obj1, T obj2) {
for (Function<T, ? extends Comparable<?>> function : functions) {
Comparable<T> res1 = (Comparable<T>) function.apply(obj1);
Comparable<T> res2 = (Comparable<T>) function.apply(obj2);
int result = res1.compareTo((T) res2);
if ( result != 0 ) {
return result;
}
}
return 0;
}
};
}
It will sort from left to right regarding the order which function parameters are placed. Warnings will be raised although. Because it's very generic.
Keep in mind that the types of the final values to be compared must implement Comparator (which primitive types like Integer already do) and you should deal with null problems (I didn't do it here to keep it short).

Sorting a list of Generics in Java?

I'm trying to implement a sorting for Generics in Java.
Here is the abstract class Function (T is my "key" in order to sort):
public abstract class Function<T extends Comparable<T>, S> {
abstract public T compute(S o);
}
Here is class Applier, whose method "apply" sorts the list according on the result of "compute":
import java.util.ArrayList;
import java.util.Iterator;
public class Applier<T extends Comparable<T>, S> {
ArrayList<S> apply(ArrayList<S> input, Function<T, S> function) {
ArrayList<T> output = new ArrayList<>();
for(Iterator<S> it = input.iterator(); it.hasNext(); ){
output.add(function.compute(it.next()));
}
T tmpTi, tmpTj;
S tmpSi, tmpSj;
for(int i=0; i<input.size(); i++) {
for(int j=i+1; j<input.size(); j++) {
if(output.get(i).compareTo(output.get(j))>0) {
tmpTi = output.get(i);
tmpTj = output.get(j);
output.remove(j);
output.remove(i);
output.add(i, tmpTi);
output.add(i, tmpTj);
tmpSi = input.get(i);
tmpSj = input.get(j);
input.remove(j);
input.remove(i);
input.add(i, tmpSj);
input.add(j, tmpSi);
}
}
}
return input;
}
}
My question is: is there a smarter way to do this sorting, maybe not with a bubblesort?
Here is also the main class:
public static void main(String[] args) {
Applier a = new Applier<>();
StringLength strlen = new StringLength();
ArrayList<String> array = new ArrayList<>();
array.add("Hola");
array.add("Man");
array.add("randomstufff");
array.add("Zerrone");
array.add("Info3a");
System.out.println("Order by length");
System.out.print("before: ");
System.out.println(array);
a.apply(array, strlen); //works on original object
System.out.print("After: ");
System.out.println(array);
Basically you want to sort an array based on some other array. You will be able to use Collections.sort if you introduce a wrapper object that contains both the values and the function results, and sort that one.
Here's a solution using Java 8 streaming API:
public class Applier<T extends Comparable<T>, S> {
static class Wrapper<T extends Comparable<T>,S> implements Comparable<Wrapper<T,S>> {
T key;
S value;
Wrapper(S s, Function<T, S> function) {
this.key = function.compute(s);
this.value = s;
}
public int compareTo(Wrapper<T,S> that) {
return key.compareTo(that.key);
}
}
ArrayList<S> apply(ArrayList<S> input, Function<T, S> function) {
S[] sorted = (S[]) IntStream.range(0, input.size())
.mapToObj(i -> new Wrapper<T,S>(input.get(i), function))
.sorted()
.map(b -> b.value).toArray();
input.clear();
input.addAll(Arrays.asList(sorted));
return input;
}
}
Note that there's an error in the way you swap elements in your Bubble Sort: When re-inserting the elements into output, you misplaced i and j. Also, instead of removing and re-inserting the elements, just use set(index, element) to overwrite the previous entry.
Also, instead of using two lists and keeping those lists in synch, better just use a Map.
public static class Applier<T extends Comparable<T>, S> {
ArrayList<S> apply(ArrayList<S> input, Function<T, S> function) {
Map<S, T> compareBy = new HashMap<>();
for (S s : input) {
compareBy.put(s, function.compute(s));
}
for(int i=0; i<input.size(); i++) {
for(int j=i+1; j<input.size(); j++) {
if (compareBy.get(input.get(i)).compareTo(compareBy.get(input.get(j))) > 0) {
S tmpS = input.get(j);
input.set(j, input.get(i));
input.set(i, tmpS);
}
}
}
return input;
}
}
And of course, sorting is already implemented in Java. So other than for learning how to code, you should always use the builtin functions. In Java 8, it's just a single line:
Collections.sort(array, Comparator.comparing(String::length));
Note, however, that Comparator.comparing will call the comparator function for each pairwise comparison (i.e. on the order of 2nlogn times for a decent sorting algorithm). If that function is computationally very expensive, you might want to cache it yourself, using a Map.
Map<String, Integer> compareBy = array.stream().collect(Collectors.toMap(s -> s, s -> s.length()));
Collections.sort(array, Comparator.comparing((String s) -> compareBy.get(s)));

Compare two sets of different types

I'm looking for a way to tell if two sets of different element types are identical if I can state one-to-one relation between those element types. Is there a standard way for doing this in java or maybe guava or apache commons?
Here is my own implementation of this task. For example, I have two element classes which I know how to compare. For simplicity, I compare them by id field:
class ValueObject {
public int id;
public ValueObject(int id) { this.id=id; }
public static ValueObject of(int id) { return new ValueObject(id); }
}
class DTO {
public int id;
public DTO(int id) { this.id=id; }
public static DTO of(int id) { return new DTO(id); }
}
Then I define an interface which does the comparison
interface TwoTypesComparator<L,R> {
boolean areIdentical(L left, R right);
}
And the actual method for comparing sets looks like this
public static <L,R> boolean areIdentical(Set<L> left, Set<R> right, TwoTypesComparator<L,R> comparator) {
if (left.size() != right.size()) return false;
boolean found;
for (L l : left) {
found = false;
for (R r : right) {
if (comparator.areIdentical(l, r)) {
found = true; break;
}
}
if (!found) return false;
}
return true;
}
Example of a client code
HashSet<ValueObject> valueObjects = new HashSet<ValueObject>();
valueObjects.add(ValueObject.of(1));
valueObjects.add(ValueObject.of(2));
valueObjects.add(ValueObject.of(3));
HashSet<DTO> dtos = new HashSet<DTO>();
dtos.add(DTO.of(1));
dtos.add(DTO.of(2));
dtos.add(DTO.of(34));
System.out.println(areIdentical(valueObjects, dtos, new TwoTypesComparator<ValueObject, DTO>() {
#Override
public boolean areIdentical(ValueObject left, DTO right) {
return left.id == right.id;
}
}));
I'm looking for the standard solution to to this task. Or any suggestions how to improve this code are welcome.
This is what I would do in your case. You have sets. Sets are hard to compare, but on top of that, you want to compare on their id.
I see only one proper solution where you have to normalize the wanted values (extract their id) then sort those ids, then compare them in order, because if you don't sort and compare you can possibly skip pass over duplicates and/or values.
Think about the fact that Java 8 allows you to play lazy with streams. So don't rush over and think that extracting, then sorting then copying is long. Lazyness allows it to be rather fast compared to iterative solutions.
HashSet<ValueObject> valueObjects = new HashSet<>();
valueObjects.add(ValueObject.of(1));
valueObjects.add(ValueObject.of(2));
valueObjects.add(ValueObject.of(3));
HashSet<DTO> dtos = new HashSet<>();
dtos.add(DTO.of(1));
dtos.add(DTO.of(2));
dtos.add(DTO.of(34));
boolean areIdentical = Arrays.equals(
valueObjects.stream()
.mapToInt((v) -> v.id)
.sorted()
.toArray(),
dtos.stream()
.mapToInt((d) -> d.id)
.sorted()
.toArray()
);
You want to generalize the solution? No problem.
public static <T extends Comparable<?>> boolean areIdentical(Collection<ValueObject> vos, Function<ValueObject, T> voKeyExtractor, Collection<DTO> dtos, Function<DTO, T> dtoKeyExtractor) {
return Arrays.equals(
vos.stream()
.map(voKeyExtractor)
.sorted()
.toArray(),
dtos.stream()
.map(dtoKeyExtractor)
.sorted()
.toArray()
);
}
And for a T that is not comparable:
public static <T> boolean areIdentical(Collection<ValueObject> vos, Function<ValueObject, T> voKeyExtractor, Collection<DTO> dtos, Function<DTO, T> dtoKeyExtractor, Comparator<T> comparator) {
return Arrays.equals(
vos.stream()
.map(voKeyExtractor)
.sorted(comparator)
.toArray(),
dtos.stream()
.map(dtoKeyExtractor)
.sorted(comparator)
.toArray()
);
}
You mention Guava and if you don't have Java 8, you can do the following, using the same algorithm:
List<Integer> voIds = FluentIterables.from(valueObjects)
.transform(valueObjectIdGetter())
.toSortedList(intComparator());
List<Integer> dtoIds = FluentIterables.from(dtos)
.transform(dtoIdGetter())
.toSortedList(intComparator());
return voIds.equals(dtoIds);
Another solution would be to use List instead of Set (if you are allowed to do so). List has a method called get(int index) that retrieves the element at the specified index and you can compare them one by one when both your lists have the same size. More on lists: http://docs.oracle.com/javase/7/docs/api/java/util/List.html
Also, avoid using public variables in your classes. A good practice is to make your variables private and use getter and setter methods.
Instantiate lists and add values
List<ValueObject> list = new ArrayList<>();
List<DTO> list2 = new ArrayList<>();
list.add(ValueObject.of(1));
list.add(ValueObject.of(2));
list.add(ValueObject.of(3));
list2.add(DTO.of(1));
list2.add(DTO.of(2));
list2.add(DTO.of(34));
Method that compares lists
public boolean compareLists(List<ValueObject> list, List<DTO> list2) {
if(list.size() != list2.size()) {
return false;
}
for(int i = 0; i < list.size(); i++) {
if(list.get(i).id == list2.get(i).id) {
continue;
} else {
return false;
}
}
return true;
}
Your current method is incorrect or at least inconsistent for general sets.
Imagine the following:
L contains the Pairs (1,1), (1,2), (2,1).
R contains the Pairs (1,1), (2,1), (2,2).
Now if your id is the first value your compare would return true but are those sets really equal? The problem is that you have no guarantee that there is at most one Element with the same id in the set because you don't know how L and R implement equals so my advise would be to not compare sets of different types.
If you really need to compare two Sets the way you described I would go for copying all Elements from L to a List and then go through R and every time you find the Element in L remove it from the List. Just make sure you use LinkedList instead of ArrayList .
You could override equals and hashcode on the dto/value object and then do : leftSet.containsAll(rightSet) && leftSet.size().equals(rightSet.size())
If you can't alter the element classes, make a decorator and have the sets be of the decorator type.

Java Lambda Stream Distinct() on arbitrary key? [duplicate]

This question already has answers here:
Java 8 Distinct by property
(34 answers)
Closed 3 years ago.
I frequently ran into a problem with Java lambda expressions where when I wanted to distinct() a stream on an arbitrary property or method of an object, but wanted to keep the object rather than map it to that property or method. I started to create containers as discussed here but I started to do it enough to where it became annoying and made a lot of boilerplate classes.
I threw together this Pairing class, which holds two objects of two types and allows you to specify keying off the left, right, or both objects. My question is... is there really no built-in lambda stream function to distinct() on a key supplier of some sorts? That would really surprise me. If not, will this class fulfill that function reliably?
Here is how it would be called
BigDecimal totalShare = orders.stream().map(c -> Pairing.keyLeft(c.getCompany().getId(), c.getShare())).distinct().map(Pairing::getRightItem).reduce(BigDecimal.ZERO, (x,y) -> x.add(y));
Here is the Pairing class
public final class Pairing<X,Y> {
private final X item1;
private final Y item2;
private final KeySetup keySetup;
private static enum KeySetup {LEFT,RIGHT,BOTH};
private Pairing(X item1, Y item2, KeySetup keySetup) {
this.item1 = item1;
this.item2 = item2;
this.keySetup = keySetup;
}
public X getLeftItem() {
return item1;
}
public Y getRightItem() {
return item2;
}
public static <X,Y> Pairing<X,Y> keyLeft(X item1, Y item2) {
return new Pairing<X,Y>(item1, item2, KeySetup.LEFT);
}
public static <X,Y> Pairing<X,Y> keyRight(X item1, Y item2) {
return new Pairing<X,Y>(item1, item2, KeySetup.RIGHT);
}
public static <X,Y> Pairing<X,Y> keyBoth(X item1, Y item2) {
return new Pairing<X,Y>(item1, item2, KeySetup.BOTH);
}
public static <X,Y> Pairing<X,Y> forItems(X item1, Y item2) {
return keyBoth(item1, item2);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
if (keySetup.equals(KeySetup.LEFT) || keySetup.equals(KeySetup.BOTH)) {
result = prime * result + ((item1 == null) ? 0 : item1.hashCode());
}
if (keySetup.equals(KeySetup.RIGHT) || keySetup.equals(KeySetup.BOTH)) {
result = prime * result + ((item2 == null) ? 0 : item2.hashCode());
}
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pairing<?,?> other = (Pairing<?,?>) obj;
if (keySetup.equals(KeySetup.LEFT) || keySetup.equals(KeySetup.BOTH)) {
if (item1 == null) {
if (other.item1 != null)
return false;
} else if (!item1.equals(other.item1))
return false;
}
if (keySetup.equals(KeySetup.RIGHT) || keySetup.equals(KeySetup.BOTH)) {
if (item2 == null) {
if (other.item2 != null)
return false;
} else if (!item2.equals(other.item2))
return false;
}
return true;
}
}
UPDATE:
Tested Stuart's function below and it seems to work great. The operation below distincts on the first letter of each string. The only part I'm trying to figure out is how the ConcurrentHashMap maintains only one instance for the entire stream
public class DistinctByKey {
public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
public static void main(String[] args) {
final ImmutableList<String> arpts = ImmutableList.of("ABQ","ALB","CHI","CUN","PHX","PUJ","BWI");
arpts.stream().filter(distinctByKey(f -> f.substring(0,1))).forEach(s -> System.out.println(s));
}
Output is...
ABQ
CHI
PHX
BWI
The distinct operation is a stateful pipeline operation; in this case it's a stateful filter. It's a bit inconvenient to create these yourself, as there's nothing built-in, but a small helper class should do the trick:
/**
* Stateful filter. T is type of stream element, K is type of extracted key.
*/
static class DistinctByKey<T,K> {
Map<K,Boolean> seen = new ConcurrentHashMap<>();
Function<T,K> keyExtractor;
public DistinctByKey(Function<T,K> ke) {
this.keyExtractor = ke;
}
public boolean filter(T t) {
return seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
}
I don't know your domain classes, but I think that, with this helper class, you could do what you want like this:
BigDecimal totalShare = orders.stream()
.filter(new DistinctByKey<Order,CompanyId>(o -> o.getCompany().getId())::filter)
.map(Order::getShare)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Unfortunately the type inference couldn't get far enough inside the expression, so I had to specify explicitly the type arguments for the DistinctByKey class.
This involves more setup than the collectors approach described by Louis Wasserman, but this has the advantage that distinct items pass through immediately instead of being buffered up until the collection completes. Space should be the same, as (unavoidably) both approaches end up accumulating all distinct keys extracted from the stream elements.
UPDATE
It's possible to get rid of the K type parameter since it's not actually used for anything other than being stored in a map. So Object is sufficient.
/**
* Stateful filter. T is type of stream element.
*/
static class DistinctByKey<T> {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
Function<T,Object> keyExtractor;
public DistinctByKey(Function<T,Object> ke) {
this.keyExtractor = ke;
}
public boolean filter(T t) {
return seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
}
BigDecimal totalShare = orders.stream()
.filter(new DistinctByKey<Order>(o -> o.getCompany().getId())::filter)
.map(Order::getShare)
.reduce(BigDecimal.ZERO, BigDecimal::add);
This simplifies things a bit, but I still had to specify the type argument to the constructor. Trying to use diamond or a static factory method doesn't seem to improve things. I think the difficulty is that the compiler can't infer generic type parameters -- for a constructor or a static method call -- when either is in the instance expression of a method reference. Oh well.
(Another variation on this that would probably simplify it is to make DistinctByKey<T> implements Predicate<T> and rename the method to eval. This would remove the need to use a method reference and would probably improve type inference. However, it's unlikely to be as nice as the solution below.)
UPDATE 2
Can't stop thinking about this. Instead of a helper class, use a higher-order function. We can use captured locals to maintain state, so we don't even need a separate class! Bonus, things are simplified so type inference works!
public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
BigDecimal totalShare = orders.stream()
.filter(distinctByKey(o -> o.getCompany().getId()))
.map(Order::getShare)
.reduce(BigDecimal.ZERO, BigDecimal::add);
You more or less have to do something like
elements.stream()
.collect(Collectors.toMap(
obj -> extractKey(obj),
obj -> obj,
(first, second) -> first
// pick the first if multiple values have the same key
)).values().stream();
Another way of finding distinct elements
List<String> uniqueObjects = ImmutableList.of("ABQ","ALB","CHI","CUN","PHX","PUJ","BWI")
.stream()
.collect(Collectors.groupingBy((p)->p.substring(0,1))) //expression
.values()
.stream()
.flatMap(e->e.stream().limit(1))
.collect(Collectors.toList());
A variation on Stuart Marks second update. Using a Set.
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Set<Object> seen = Collections.newSetFromMap(new ConcurrentHashMap<>());
return t -> seen.add(keyExtractor.apply(t));
}
We can also use RxJava (very powerful reactive extension library)
Observable.from(persons).distinct(Person::getName)
or
Observable.from(persons).distinct(p -> p.getName())
To answer your question in your second update:
The only part I'm trying to figure out is how the ConcurrentHashMap maintains only one instance for the entire stream:
public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
In your code sample, distinctByKey is only invoked one time, so the ConcurrentHashMap created just once. Here's an explanation:
The distinctByKey function is just a plain-old function that returns an object, and that object happens to be a Predicate. Keep in mind that a predicate is basically a piece of code that can be evaluated later. To manually evaluate a predicate, you must call a method in the Predicate interface such as test. So, the predicate
t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null
is merely a declaration that is not actually evaluated inside distinctByKey.
The predicate is passed around just like any other object. It is returned and passed into the filter operation, which basically evaluates the predicate repeatedly against each element of the stream by calling test.
I'm sure filter is more complicated than I made it out to be, but the point is, the predicate is evaluated many times outside of distinctByKey. There's nothing special* about distinctByKey; it's just a function that you've called one time, so the ConcurrentHashMap is only created one time.
*Apart from being well made, #stuart-marks :)
You can use the distinct(HashingStrategy) method in Eclipse Collections.
List<String> list = Lists.mutable.with("ABQ", "ALB", "CHI", "CUN", "PHX", "PUJ", "BWI");
ListIterate.distinct(list, HashingStrategies.fromFunction(s -> s.substring(0, 1)))
.each(System.out::println);
If you can refactor list to implement an Eclipse Collections interface, you can call the method directly on the list.
MutableList<String> list = Lists.mutable.with("ABQ", "ALB", "CHI", "CUN", "PHX", "PUJ", "BWI");
list.distinct(HashingStrategies.fromFunction(s -> s.substring(0, 1)))
.each(System.out::println);
HashingStrategy is simply a strategy interface that allows you to define custom implementations of equals and hashcode.
public interface HashingStrategy<E>
{
int computeHashCode(E object);
boolean equals(E object1, E object2);
}
Note: I am a committer for Eclipse Collections.
Set.add(element) returns true if the set did not already contain element, otherwise false.
So you can do like this.
Set<String> set = new HashSet<>();
BigDecimal totalShare = orders.stream()
.filter(c -> set.add(c.getCompany().getId()))
.map(c -> c.getShare())
.reduce(BigDecimal.ZERO, BigDecimal::add);
If you want to do this parallel, you must use concurrent map.
It can be done something like
Set<String> distinctCompany = orders.stream()
.map(Order::getCompany)
.collect(Collectors.toSet());

Categories

Resources