Get the maximum value using stream for Map - java-8

I have a class called Test. This class has a method called getNumber which returns an int value.
public class Test{
.
.
.
.
public int getNumber(){
return number;
}
}
Also I have a HashMap which the key is a Long and the value is a Test object.
Map<Long, Test> map = new HashMap<Long, Test>();
I want to print the key and also getNumber which has a maximum getNumber using a Stream Line code.
I can print the maximum Number via below lines
final Comparator<Test> comp = (p1, p2) -> Integer.compare(p1.getNumber(), p2.getNumber());
map.entrySet().stream().map(m -> m.getValue())
.max(comp).ifPresent(d -> System.out.println(d.getNumber()));
However my question is How can I return the key of the maximum amount? Can I do it with one round using stream?

If I understood you correctly:
Entry<Long, Test> entry = map.entrySet()
.stream()
.max(Map.Entry.comparingByValue(Comparator.comparingInt(Test::getNumber)))
.get();

If you want to find the key-value pair corresponding to the maximum 'number' value in the Test instances, you can use Collections.max() combined with a comparator that compares the entries with this criteria.
import static java.util.Comparator.comparingInt;
...
Map.Entry<Long, Test> maxEntry =
Collections.max(map.entrySet(), comparingInt(e -> e.getValue().getNumber()))
If you want to use the stream way, then remove the mapping (because you lost the key associated with the value), and provide the same comparator:
map.entrySet()
.stream()
.max(comparingInt(e -> e.getValue().getNumber()))
.ifPresent(System.out::println);
Note that there is a small difference in both snippets, as the first one will throw a NoSuchElementException if the provided map is empty.

Related

Avoid counting values of Ints with for loop in Kotlin

I have a list of A class objects
data class A{
val abc: Abc
val values: Int?
}
val list = List<A>
If I want to count how many objects I have in list I use:
val count= a.count()
or val count= a.count(it -> {})
How to append all values in the list of objects A avoiding for loop? Generaly Im looking for proper kotlin syntax with avoiding code below
if (a!= null) {
for (i in list) {
counter += i.values!!
}
}
Either use sumBy or sum in case you have a list of non-nullable numbers already available, i.e.:
val counter = list.sumBy { it.values ?: 0 }
// or
val counter = extractedNonNullValues.sum()
The latter only makes sense if you already mapped your A.values before to a list of non-nullable values, e.g. something like:
val extractedNonNullValues= list.mapNotNull { it.values } // set somewhere else before because you needed it...
If you do not need such an intermediate extractedNonNullValues-list then just go for the sumBy-variant.
I don't see you doing any appending to a list in the question. Based on your for loop I believe what you meant was "How do I sum properties of objects in my list". If that's the case you can use sumBy, the extension function on list that takes a labmda: ((T) -> Int) and returns an Int like so:
val sum = list.sumBy { a -> a.values ?: 0 }
Also, calling an Int property values is pretty confusing, I think it should be called value. The plural indicates a list...
On another note, there is a possible NPE in your original for loop. Avoid using !! on nullable values as, if the value is null, you will get an NPE. Instead, use null coalescing (aka elvis) operator to fall back to a default value ?: - this is perfectly acceptable in a sum function. If the iteration is not to do with summing, you may need to handle the null case differently.

Building binary tree using Java Stream. Is it possible in Java Stream sorting while reduce?

I want to build a Huffman tree from input string using Java Stream.
This is how I do it right now.
Class MyNode with all needed Constructors:
public static class MyNode {
Character value;
MyNode left;
MyNode right;
long freq;
...
}
Reading a line and getting List of MyNodes:
Scanner scan = new Scanner(System.in);
String input = scan.next();
List<MyNode> listOfNodes = input.chars().boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet()
.stream().sorted(Comparator.comparingLong(Map.Entry::getValue))
.map(x -> new MyNode((char)x.getKey().intValue(), x.getValue()))
.collect(Collectors.toList());
This while loop I want to replace with something from Stream:
while (listOfNodes.size() > 1) {
MyNode first = listOfNodes.get(0);
MyNode second = listOfNodes.get(1);
listOfNodes.remove(first);
listOfNodes.remove(second);
listOfNodes.add(new MyNode(first.freq + second.freq, first, second));
listOfNodes.sort(Comparator.comparingLong(MyNode::getFreq));
}
In while loop I build tree like this
The first idea was to use Stream reduce, but then I need to sort resulting list after every reduce.
This is not a task that benefits from using the Stream API. Still, there are ways to improve it.
Sorting the entire list just for the sake of inserting a single element, bear an unnecessary overhead. Since the list is sorted to begin with, you can use binary search to efficiently find the correct insertion position so that the list stays sorted:
while(listOfNodes.size() > 1) {
MyNode first = listOfNodes.remove(0), second = listOfNodes.remove(0);
MyNode newNode = new MyNode(first.freq + second.freq, first, second);
int pos = Collections.binarySearch(listOfNodes, newNode,
Comparator.comparingLong(MyNode::getFreq));
listOfNodes.add(pos<0? -pos-1: pos, newNode);
}
Note that you could make this code more efficient by reversing the order so that you will remove from the end of the list (which will be an ArrayList in practice).
But the better alternative is to use a data structure which is sorted to begin with, e.g.
PriorityQueue<MyNode> queueOfNodes = input.chars().boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.map(x -> new MyNode((char)x.getKey().intValue(), x.getValue()))
.collect(Collectors.toCollection(
() -> new PriorityQueue<>(Comparator.comparingLong(MyNode::getFreq))));
MyNode result = queueOfNodes.remove();
while(!queueOfNodes.isEmpty()) {
MyNode second = queueOfNodes.remove();
queueOfNodes.add(new MyNode(result.freq + second.freq, result, second));
result = queueOfNodes.remove();
}

Producing histogram Map for IntStream raises compile-time-error

I'm interested in building a Huffman Coding prototype. To that end, I want to begin by producing a histogram of the characters that make up an input Java String. I've seen many solutions on SO and elsewhere (e.g:here that depend on using the collect() methods for Streams as well as static imports of Function.identity() and Collectors.counting() in a very specific and intuitive way.
However, when using a piece of code eerily similar to the one I linked to above:
private List<HuffmanTrieNode> getCharsAndFreqs(String s){
Map<Character, Long> freqs = s.chars().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
return null;
}
I receive a compile-time-error from Intellij which essentially tells me that there is no arguments to collect that conforms to a Supplier type, as required by its signature:
Unfortunately, I'm new to the Java 8 Stream hierarchy and I'm not entirely sure what the best course of action for me should be. In fact, going the Map way might be too much boilerplate for what I'm trying to do; please advise if so.
The problem is that s.chars() returns an IntStream - a particular specialization of Stream and it does not have a collect that takes a single argument; it's collect takes 3 arguments. Obviously you can use boxed and that would transform that IntStream to Stream<Integer>.
Map<Integer, Long> map = yourString.codePoints()
.boxed()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()));
But now the problem is that you have counted code-points and not chars. If you absolutely know that your String is made from characters in the BMP, you can safely cast to char as shown in the other answer. If you are not - things get trickier.
In that case you need to get the single unicode code point as a character - but it might not fit into a Java char - that has 2 bytes; and a unicode character can be up to 4 bytes.
In that case your map should be Map<String, Long> and not Map<Character, Long>.
In java-9 with the introduction of supported \X (and Scanner#findAll) this is fairly easy to do:
String sample = "A" + "\uD835\uDD0A" + "B" + "C";
Map<String, Long> map = scan.findAll("\\X")
.map(MatchResult::group)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(map); // {A=1, B=1, C=1, 𝔊=1}
In java-8 this would be a bit more verbose:
String sample = "AA" + "\uD835\uDD0A" + "B" + "C";
Map<String, Long> map = new HashMap<>();
Pattern p = Pattern.compile("\\P{M}\\p{M}*+");
Matcher m = p.matcher(sample);
while (m.find()) {
map.merge(m.group(), 1L, Long::sum);
}
System.out.println(map); // {A=2, B=1, C=1, 𝔊=1}
The String.chars() method returns an IntStream. You probably want to convert it to a Stream<Character> via:
s.chars().mapToObj(c -> (char)c)
As already pointed, you could transform the stream to primitive types to Object types.
s.chars().boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

Java8 style for comparing arrays? (Streams and Math3)

I'm just beginning to learn Java8 streams and Apache commons Math3 at the same time, and looking for missed opportunities to simplify my solution for comparing instances for equality. Consider this Math3 RealVector:
RealVector testArrayRealVector =
new ArrayRealVector(new double [] {1d, 2d, 3d});
and consider this member variable containing boxed doubles, plus this copy of it as an array list collection:
private final Double [] m_ADoubleArray = {13d, 14d, 15d};
private final Collection<Double> m_CollectionArrayList =
new ArrayList<>(Arrays.asList(m_ADoubleArray));
Here is my best shot at comparing these in a functional style in a JUnit class (full gist here), using protonpack from codepoetix because I couldn't find zip in the Streams library. This looks really baroque to my eyes and I wonder whether I've missed ways to make this shorter, faster, simpler, better because I'm just beginning to learn this stuff and don't know much.
// Make a stream out of the RealVector:
DoubleStream testArrayRealVectorStream =
Arrays.stream(testArrayRealVector.toArray());
// Check the type of that Stream
assertTrue("java.util.stream.DoublePipeline$Head" ==
testArrayRealVectorStream.getClass().getTypeName());
// Use up the stream:
assertEquals(3, testArrayRealVectorStream.count());
// Old one is used up; make another:
testArrayRealVectorStream = Arrays.stream(testArrayRealVector.toArray());
// Make a new stream from the member-var arrayList;
// do arithmetic on the copy, leaving the original unmodified:
Stream<Double> collectionStream = getFreshMemberVarStream();
// Use up the stream:
assertEquals(3, collectionStream.count());
// Stream is now used up; make new one:
collectionStream = getFreshMemberVarStream();
// Doesn't seem to be any way to use zip on the real array vector
// without boxing it.
Stream<Double> arrayRealVectorStreamBoxed =
testArrayRealVectorStream.boxed();
assertTrue(zip(
collectionStream,
arrayRealVectorStreamBoxed,
(l, r) -> Math.abs(l - r) < DELTA)
.reduce(true, (a, b) -> a && b));
where
private Stream<Double> getFreshMemberVarStream() {
return m_CollectionArrayList
.stream()
.map(x -> x - 12.0);
}
Again, here is a gist of my entire JUnit test class.
It seems you are trying to bail in Streams at all cost.
If I understand you correctly, you have
double[] array1=testArrayRealVector.toArray();
Double[] m_ADoubleArray = {13d, 14d, 15d};
as starting point. Then, the first thing you can do is to verify the lengths of these arrays:
assertTrue(array1.length==m_ADoubleArray.length);
assertEquals(3, array1.length);
There is no point in wrapping the arrays into a stream and calling count() and, of course, even less in wrapping an array into a collection to call stream().count() on it. Note that if your starting point is a Collection, calling size() will do as well.
Given that you already verified the length, you can simply do
IntStream.range(0, 3).forEach(ix->assertEquals(m_ADoubleArray[ix]-12, array1[ix], DELTA));
to compare the elements of the arrays.
or when you want to apply arithmetic as a function:
// keep the size check as above as the length won’t change
IntToDoubleFunction f=ix -> m_ADoubleArray[ix]-12;
IntStream.range(0, 3).forEach(ix -> assertEquals(f.applyAsDouble(ix), array1[ix], DELTA));
Note that you can also just create a new array using
double[] array2=Arrays.stream(m_ADoubleArray).mapToDouble(d -> d-12).toArray();
and compare the arrays similar to above:
IntStream.range(0, 3).forEach(ix -> assertEquals(array1[ix], array2[ix], DELTA));
or just using
assertArrayEquals(array1, array2, DELTA);
as now both arrays have the same type.
Don’t think about that temporary three element array holding the intermediate result. All other attempts consume far more memory…

How to retainAll of List of Lists using stream reduce

I faced following problem. I have a list of lists which i simply want to retainAll. I'm trying to do with streams
private List<List<Long>> ids = new ArrayList<List<Long>>();
// some ids.add(otherLists);
List<Long> reduce = ids.stream().reduce(ids.get(0), (a, b) -> a.addAll(b));
unfortunately I got the error
Error:(72, 67) java: incompatible types: bad return type in lambda expression
boolean cannot be converted to java.util.List<java.lang.Long>
If you want to reduce (I think you mean flatten by that) the list of lists, you should do it like this:
import static java.util.stream.Collectors.toList
...
List<Long> reduce = ids.stream().flatMap(List::stream).collect(toList());
Using reduce, the first value should be the identity value which is not the case in your implementation, and your solution will produce unexpected results when running the stream in parallel (because addAll modifies the list in place, and in this case the identity value will be the same list for partial results).
You'd need to copy the content of the partial result list, and add the other list in it to make it working when the pipeline is run in parallel:
List<Long> reduce = ids.parallelStream().reduce(new ArrayList<>(), (a, b) -> {
List<Long> list = new ArrayList<Long>(a);
list.addAll(b);
return list;
});
addAll returns a boolean, not the union of the two lists. You want
List<Long> reduce = ids.stream().reduce(ids.get(0), (a, b) -> {
a.addAll(b);
return a;
});

Resources