A memo on how to use the Google MµG library, a utility library for Java 8.
dependencies {
compile 'com.google.mug:mug:1.12'
}
BiStream
Stream with key and value.
BiStreamUse the static method of BiStream.
biStream()
var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.biStream( input )
.mapValues( e -> e.toUpperCase() )
.toMap();
assertThat( result ).containsExactly( "foo", "FOO", "bar", "BAR", "buz", "BUZ" );
Wrap a regular Stream. When wondering what to use, it seems that it is to call an operation like mapKeys () afterwards.
var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.biStream( input, Function.identity(), e -> e.toUpperCase() )
.toMap();
assertThat( result ).containsExactly( "foo", "FOO", "bar", "BAR", "buz", "BUZ" );
A version that takes a mapping function as an argument.
from()
var input = Map.of( "foo", "123", "bar", "456", "buz", "789" );
var result = BiStream.from( input )
.map( ( i, v ) -> i + ":" + v )
.collect( toList() );
assertThat( result ).containsExactly( "foo:123", "bar:456", "buz:789" );
Create a BiStream with the map key and value as a pair.
indexed()
var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.indexed( input )
.map( ( i, v ) -> i + ":" + v )
.collect( toList() );
assertThat( result ).containsExactly( "0:foo", "1:bar", "2:buz" );
Add an index starting from 0. This is nice because it's built into the standard APIs of most programming languages.
The return type is BiStream <Integer, V>. Primitive types are not supported as expected.
neighbors()
var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.neighbors( input )
.map( ( i, v ) -> i + ":" + v )
.collect( toList() );
assertThat( result ).containsExactly( "foo:bar", "bar:buz" );
Pair two adjacent values.
of()
var result = BiStream.of( "foo", "123", "bar", "456" )
.map( ( i, v ) -> i + ":" + v )
.collect( toList() );
assertThat( result ).containsExactly( "foo:123", "bar:456" );
Similar to the JDK standard ʻof ()`.
zip()
var input1 = Stream.of( "foo", "bar", "buz" );
var input2 = Stream.of( "hoge", "piyo", "fuga" );
var result = BiStream.zip( input1, input2 )
.map( ( t, u ) -> t + ":" + u )
.collect( toList() );
assertThat( result ).containsExactly( "foo:hoge", "bar:piyo", "buz:fuga" );
Combine the two Streams. I always made this myself. To be honest, the level I want you to add to the standard library.
However, note that if the size of Stream is different, the remainder will be ignored according to the smaller one.
var input1 = Stream.of( "foo", "bar", "buz" );
var input2 = Stream.of( "hoge", "piyo" );
var result = BiStream.zip( input1, input2 )
.map( ( t, u ) -> t + ":" + u )
.collect( toList() );
assertThat( result ).containsExactly( "foo:hoge", "bar:piyo" );
BiStreamStream operations, operations for keys and values such asfilterKeys ()andmapValues ()have been added.append()
var result = BiStream.of( "foo", "123" ).append( "bar", "456" )
.map( ( t, u ) -> t + ":" + u )
.collect( toList() );
assertThat( result ).containsExactly( "foo:123", "bar:456" );
Add a value after it.
inverse()
var result = BiStream.of( "foo", "123", "bar", "456" )
.inverse()
.map( ( t, u ) -> t + ":" + u )
.collect( toList() );
assertThat( result ).containsExactly( "123:foo", "456:bar" );
Exchange values for keys. I want you to add it to the standard Stream ...
MoreStream
Provides extensions not found in the JDK or Guava.
generate()
var result = MoreStreams.generate( 1, n -> n >= 9 ? Stream.empty() : Stream.of( n + 1 ) )
.collect( toList() );
assertThat( result ).containsExactly( 1, 2, 3, 4, 5, 6, 7, 8, 9 );
A method for generating a * finite * stream. Stop generation when an empty stream is returned. Is there any use for it?
flatten()
var input = Stream.of(
Stream.of( "foo", "bar", "buz" ),
Stream.of( "hoge", "piyo", "fuga" )
);
var result = MoreStreams.flatten( input )
.collect( toList() );
assertThat( result ).containsExactly( "foo", "bar", "buz", "hoge", "piyo", "fuga" );
The same process should be possible with flatMap, but there is this method because it cannot handle infinite streams due to a Java bug. It seems that it was fixed in JDK 10.
dice()
var input = Stream.of( "foo", "bar", "buz", "hoge", "piyo" );
var result = MoreStreams.dice( input, 2 )
.collect( toList() );
assertThat( result.size() ).isEqualTo( 3 );
var list1 = result.get( 0 );
assertThat( list1 ).containsExactly( "foo", "bar" );
var list2 = result.get( 1 );
assertThat( list2 ).containsExactly( "buz", "hoge" );
var list3 = result.get( 2 );
assertThat( list3 ).containsExactly( "piyo" );
List Streams by specified size and put them together in a newStream <List> .
For sequential streams, it is guaranteed that a list of the specified size will be returned except at the end of the stream. Not guaranteed for parallel streams.
By the way, I learned for the first time that such an operation is called dice. How is it different from window?
iterateOnce()
var input = Stream.of( "foo", "bar", "buz" );
for ( var e : MoreStreams.iterateOnce( input ) ) {
assertThat( e ).isAnyOf( "foo", "bar", "buz" );
}
A method for iterating a stream with a for statement. Maybe I won't use it for the rest of my life.
iterateThrough()
expect(() -> {
MoreStreams.iterateThrough( Stream.of( "foo" ), e -> {
throw new Exception( "Checked Exception!" );
});
}).throwsException( e -> {
assertThat( e ).hasMessageThat().isEqualTo( "Checked Exception!" );
});
It's basically the same as forEach (), except that you can throw a checked exception.
Retryer
A process for retrying a process that may fail. I think it's convenient, but most of the processes that require retries are provided with a retry function in the first place, so it's surprisingly useless.
Retryer., ʻifReturns (), etc.Delay. In most cases, you can use ʻexponentialBackoff ()` to double the retry interval.retryBlockingly (), retry (), retryAsync ().var result = new Retryer()
.upon( RuntimeException.class, Retryer.Delay.ofMillis( 100 ).exponentialBackoff( 2, 2 ) )
.retryBlockingly( this::mayFail );
assertThat( result ).isEqualTo( "success" );
CompletionStage<String> future = new Retryer()
.upon( RuntimeException.class, Retryer.Delay.ofMillis( 100 ).exponentialBackoff( 2, 2 ) )
.retry( this::mayFail, Executors.newSingleThreadScheduledExecutor() );
future.thenAccept( result -> {
assertThat( result ).isEqualTo( "success" );
});
To be honest, the asynchronous API is subtle.
Maybe
A class for wrapping processing that may raise an exception. Something like a version of java.util.Optional with the ability to handle exceptions. Is Try relatively close in Scala?
The main purpose seems to be to handle checked exceptions in streams easily.
maybe()
The basic method for creating a Maybe instance. Accepts various functional interfaces.
var result = IntStream.range( 1, 10 ).boxed()
.map( Maybe.maybe( e -> {
if ( e <= 5 ) {
return e;
} else {
throw new Exception();
}
} ) )
.map( m -> m.orElse( e -> Integer.valueOf( 0 ) ) )
.collect( toList() );
assertThat( result ).containsExactly( 1, 2, 3, 4, 5, 0, 0, 0, 0 );
The value that caused the exception in ʻorElse ()is converted to0`.
expect(() -> {
Maybe.maybe( () -> { throw new RuntimeException( "Unchecked!" );} );
}).throwsException( e -> {
assertThat( e ).hasMessageThat().isEqualTo( "Unchecked!" );
});
Unchecked exceptions are rethrown as is. Caution. It's not that it can be used for anything other than streams, but due to this limitation, I think it's better to use a dedicated library.
byValue()
var result = IntStream.range( 1, 10 ).boxed()
.map( Maybe.maybe( e -> {
if ( e <= 5 ) {
return e;
} else {
throw new Exception();
}
} ) )
.filter( Maybe.byValue( n -> n % 2 == 0 ) )
.map( m -> m.orElse( e -> Integer.valueOf( 0 ) ) )
.collect( toList() );
assertThat( result ).containsExactly( 2, 4, 0, 0, 0, 0 );
Since filter () will receive Maybe, there is a method to unwrap it. As you can see, if an exception occurs, it moves on to the subsequent processing.

catching()
var result = IntStream.range( 1, 10 ).boxed()
.map( Maybe.maybe( e -> {
if ( e <= 5 ) {
return e;
} else {
throw new Exception();
}
} ) )
.flatMap( m -> m.catching( e -> {} ) )
.collect( toList() );
assertThat( result ).containsExactly( 1, 2, 3, 4, 5 );
Use when you just want to ignore the value.
Funnel
It's quite a niche to use, but it seems convenient if you get hooked.
, Stream`) to another valueFunnel
var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> {
assertThat( data ).containsExactly( "batch1", "batch2" );
return data.stream().map( String::toUpperCase ).collect( toList() );
} );
var input = List.of( "local1", "batch1", "local2", "batch2" );
for ( var i : input ) {
if ( i.startsWith( "local" ) ) {
funnel.add( i );
} else {
batch.accept( i );
}
}
var result = funnel.run();
assertThat( result ).containsExactly( "local1", "BATCH1", "local2", "BATCH2" );
Funnelthrough () method.Funnel.add () for values to be processed individually.Funnel.Batch.accept ().Funnel.run ().Note that ʻaccept ()` has an overload that takes a function that converts the result as an argument.
var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> List.of() );
batch.accept( "batch1" );
expect(() -> {
funnel.run();
}).throwsException( e -> {
assertThat( e ).isInstanceOf( IllegalStateException.class );
});
Obviously, if the size of the batch return list is different from the input, you will get an error.
var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> List.of() );
batch.accept( "batch1" );
expect(() -> {
funnel.run();
}).throwsException( e -> {
assertThat( e ).isInstanceOf( IllegalStateException.class );
});
The order depends on the order of batch processing. It is necessary to ensure that the process returns in the same order as the input order.
var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> {
var newList = new ArrayList<String>( data );
Collections.reverse( newList );
return newList;
} );
batch.accept( "batch1" );
batch.accept( "batch2" );
var result = funnel.run();
assertThat( result ).containsExactly( "batch2", "batch1" );
Parallelizer
As the name suggests, parallelize processing.
var input = Stream.of( "foo", "bar", "buz" );
var result = Collections.synchronizedList( new ArrayList<String>() );
new Parallelizer( executor, 3 )
.parallelize( input, result::add );
assertThat( result ).containsExactly( "foo", "bar", "buz" );
Parallelizer. The arguments are ʻExecutorService to execute the process and the number of tasks that can be executed at the same time. Is it basically the same as the number of threads in ʻExecutor Service?parallelize () or parallelizeUninterruptibly (). The first argument is Stream or ʻIterator`, which is the input data, and the second argument is the processing to be performed.The process is blocked until it finishes. If an exception occurs in the middle, subsequent processing is interrupted.
According to the README
Parallel streams are for CPU-bound tasks. JDK has built-in magic to optimally use the available cores. Parallelizer is for IO-bound tasks.
It seems that it should be used for file input / output, external API call, etc.
BiStream and MoreStreams are convenientRecommended Posts