Created
June 16, 2021 11:41
-
-
Save marco-brandizi/7c57a6929de4d650d5ea13e0ae147c95 to your computer and use it in GitHub Desktop.
2ndBestStreams
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package info.marcobrandizi.test.jdk11; | |
/* | |
* These deps are needed: | |
* org.apache.commons:commons-lang3:3.9 | |
* com.machinezoo.noexception:noexception:1.3.2 | |
*/ | |
import static org.junit.Assert.assertEquals; | |
import static org.junit.Assert.assertTrue; | |
import java.util.Comparator; | |
import java.util.stream.IntStream; | |
import java.util.stream.Stream; | |
import org.apache.commons.lang3.RandomUtils; | |
import org.apache.commons.lang3.tuple.Pair; | |
import org.junit.Test; | |
import com.machinezoo.noexception.Exceptions; | |
/** | |
* Shows Stream reductions by means of finding the highest and second best in a | |
* stream of {@link Comparable}. | |
* | |
* @author brandizi | |
* <dl><dt>Date:</dt><dd>14 Jun 2021</dd></dl> | |
* | |
*/ | |
public class StreamMaxesTest | |
{ | |
/** | |
* Computes the highest and the second highest value. | |
* No null values accepted. | |
* | |
*/ | |
public <T extends Comparable<T>> Pair<T, T> maxes ( Stream<T> values ) | |
{ | |
// Nulls are used as the lowest, so you're supposed to not use nulls | |
final Comparator<T> cmp = Comparator.nullsFirst ( (x, y) -> x.compareTo ( y ) ); | |
@SuppressWarnings ( "unchecked" ) | |
var result = values | |
.reduce ( | |
// Identity, or initial value, initially passed to the accumulator | |
Pair.of ( null, null ), | |
// The accumulator, eats v into m | |
( Pair<T, T> m, T v ) -> | |
{ | |
if ( cmp.compare ( v, m.getLeft () ) > 0 ) return Pair.of ( v, m.getLeft () ); | |
if ( cmp.compare ( v, m.getRight () ) > 0 && cmp.compare ( v, m.getLeft () ) != 0 ) | |
return Pair.of ( m.getLeft (), v ); | |
return m; | |
}, | |
// The combiner, when backtracking from partial results, merges them | |
( Pair<T,T> m1, Pair<T,T> m2 ) -> | |
{ | |
T nm1 = null; | |
T nm2 = null; | |
for ( var m: (T[]) new Comparable[] { m1.getLeft (), m1.getRight (), m2.getLeft (), m2.getRight () } ) | |
{ | |
if ( cmp.compare ( m, nm1 ) > 0 ) { nm2 = nm1; nm1 = m; } | |
else if ( cmp.compare ( m, nm2 ) > 0 && cmp.compare ( nm1, m ) != 0 ) nm2 = m; | |
} | |
return Pair.of ( nm1, nm2 ); | |
} | |
); | |
if ( result.getRight () == null ) return Pair.of ( result.getLeft (), result.getLeft () ); | |
return result; | |
} | |
@Test | |
public void testMaxes () | |
{ | |
var m = maxes ( Stream.of ( 1, 10, 2, 7, 102, 5, 55, 55, 0 ).parallel () ); | |
assertEquals ( "maxes() is wrong!", Pair.of ( 102, 55 ), m ); | |
} | |
@Test | |
public void testMaxesAllTheSame () | |
{ | |
var m = maxes ( Stream.of ( 0, 0, 0 ) ); | |
assertEquals ( "maxes() is wrong!", Pair.of ( 0, 0 ), m ); | |
} | |
@Test | |
public void testMaxesSingleton () | |
{ | |
var m = maxes ( Stream.of ( 0 ) ); | |
assertEquals ( "maxes() is wrong!", Pair.of ( 0, 0 ), m ); | |
} | |
@Test | |
public void testMaxesEmpty () | |
{ | |
var m = maxes ( Stream.of ( new String[ 0 ] ) ); | |
assertEquals ( "maxes() is wrong!", Pair.of ( null, null ), m ); | |
} | |
@Test | |
public void testMaxesStrings () | |
{ | |
var m = maxes ( Stream.of ( "Hello", "World", "I am", "Marco" ) ); | |
assertEquals ( "maxes() is wrong!", Pair.of ( "World", "Marco" ), m ); | |
} | |
@Test | |
public void testMany () | |
{ | |
var s = IntStream.rangeClosed ( 0, (int) 10E6 ) | |
.parallel () | |
.boxed (); | |
var m = maxes ( s ); | |
assertEquals ( "maxes() is wrong!", Pair.of ( (int)10E6, (int)10E6 - 1 ), m ); | |
} | |
@Test | |
public void testManyRnd () | |
{ | |
var s = Stream.generate ( () -> RandomUtils.nextDouble (0d, 1d) ) | |
.parallel () | |
// Without this delay, the sequential version is faster than the parallel one | |
.peek ( Exceptions.sneak ().consumer ( v -> Thread.sleep ( (long) ( v * 10 ) ) ) ) | |
.limit ( (long) 5000 ); | |
var m = maxes ( s ); | |
var m1 = m.getLeft (); | |
var m2 = m.getRight (); | |
assertTrue ( "maxes() is wrong!", m1 > m2 && m1 >= 0 && m1 < 1 && m2 >= 0 && m2 < 1 ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment