Skip to content

Instantly share code, notes, and snippets.

@marco-brandizi
Created June 16, 2021 11:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marco-brandizi/7c57a6929de4d650d5ea13e0ae147c95 to your computer and use it in GitHub Desktop.
Save marco-brandizi/7c57a6929de4d650d5ea13e0ae147c95 to your computer and use it in GitHub Desktop.
2ndBestStreams
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