Código funcional em Java: Superando o hype
Eder Ignatowicz @ederign
Cdigo funcional em Java: Superando o hype Eder Ignatowicz @ederign - - PowerPoint PPT Presentation
Cdigo funcional em Java: Superando o hype Eder Ignatowicz @ederign Lambda Expressions 101 Dora Bento <3 Jesse "Pinkman" public Pug( String nome, String color, Integer size ) { this.nome = nome; this.color = color;
Código funcional em Java: Superando o hype
Eder Ignatowicz @ederign
Lambda Expressions 101
Jesse "Pinkman"
public Pug( String nome, String color, Integer size ) { this.nome = nome; this.color = color; this.weight = size; } Pug dora = new Pug( "Dora", "abricot", 10 ); Pug bento = new Pug( "Bento", "abricot", 13 ); Pug jesse = new Pug( "Jesse", "black", 9 );
Requisito: Filtrar todos os pugs de cor “abricot" da lista
Filtrar todos os pugs de cor “abricot" da lista
private static List<Pug> filterAbricotPugs( List<Pug> pugs ) { List<Pug> abricots = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( "abricot" ) ) { abricots.add( pug ); } } return abricots; }
List<Pug> pugs = Arrays.asList( dora, bento, jesse ); List<Pug> abricot = filterAbricotPugs( pugs );
Pug{nome='Dora', color='abricot', weight=10} Pug{nome='Bento', color='abricot', weight=13}
Novo Requisito: :) Filtrar todos os pugs de cor “black" da lista
private static List<Pug> filterAbricotPugs( List<Pug> pugs ) { List<Pug> abricots = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( "abricot" ) ) { abricots.add( pug ); } } return abricots; }
private static List<Pug> filterBlackPugs( List<Pug> pugs ) { List<Pug> abricots = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( "black" ) ) { abricots.add( pug ); } } return abricots; }
Filtrar todos os pugs de cor “black" da lista
private static List<Pug> filterPugByColor( List<Pug> pugs, String color ) { List<Pug> coloured = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getColor().equals( color ) ) { coloured.add( pug ); } } return coloured; }
List<Pug> black = filterPugByColor( pugs, "black" ); black.forEach( System.out::println );
Pug{nome='Jesse', color='black', weight=9}
Novo Requisito: :) Filtrar todos os pugs com sobrepeso (>10 kg)
Filtrar todos os pugs com sobrepeso (>10 kg)
private static List<Pug> filterPugBySize( List<Pug> pugs, int size ) { List<Pug> fat = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getWeight() > size ) { fat.add( pug ); } } return fat; }
List<Pug> fat = filterPugBySize( pugs, 10 ); fat.forEach( System.out::println ); Pug{nome='Bento', color='abricot', weight=13}
private static List<Pug> filterPugs( List<Pug> pugs, String color, int weight, boolean flag ) { List<Pug> result = new ArrayList<>(); for ( Pug pug : pugs ) { if ( ( flag && pug.getColor().equals( color ) ) || ( !flag && pug.getWeight() > weight ) ) { result.add( pug ); } } return result; }
Refatorador :D
List<Pug> result = filterPugs(pugs, "black", 0, true); List<Pug> result1 = filterPugs(pugs, "", 10, false);
Behavior Parametrization
Behavior Parametrization
public interface PugPredicate { boolean test(Pug pug); } class BlackPugPredicate implements PugPredicate{ @Override public boolean test( Pug pug ) { return pug.getColor().equals( "black" ); } } class FatPugPredicate implements PugPredicate{ @Override public boolean test( Pug pug ) { return pug.getWeight()>10; } }
Predicate
List<Pug> black = filterPug( pugs, new BlackPugPredicate() ); List<Pug> fat = filterPug( pugs, new FatPugPredicate() ); fat.forEach( System.out::println ); Pug{nome='Bento', color='abricot', weight=13}
Problemas?
Classes anônimas <3
List<Pug> abricotsNotFat = filterPug( pugs, new PugPredicate() { @Override public boolean test( Pug pug ) { return pug.getColor().equals( "abricot" ) && pug.getWeight() <= 10; } } ); abricotsNotFat.forEach( System.out::println );
Pug{nome='Dora', color='abricot', weight=10}
Expressões Lambda <3
(Pug p1,Pug p2) -> p1.getWeight().compareTo(p2.getWeight())
Parâmetros do Lambda
"Arrow" Corpo do Lambda
class BlackPugPredicate implements PugPredicate{ @Override public boolean test( Pug pug ) { return "black".equals( pug.getColor() ) ); } } blacks = filterPug( pugs, new BlackPugPredicate() ); blacks = filterPug( pugs, (Pug pug) ->"black".equals( pug.getColor() ) );
Posso modificar mais um pouco?
public interface Predicate<T>{ boolean test(T t); } public static <T> List<T> filter (List<T> list, Predicate<T> p){ List<T> result = new ArrayList<>(); for(T e: list){ if(p.test(e)){ result.add(e); } } return result; }
List<Pug> blacks = filter( pugs, (Pug pug) ->"black".equals( pug.getColor() ) ); List<String> pares = filter(numbers, (Integer i) -> i % 2 == 0);
Predicate<Pug> fatPredicate = d -> d.getWeight() > 9; Predicate<Pug> abricotPredicate1 = d -> d.getColor().equalsIgnoreCase( "abricot" ); Predicate<Pug> abricotAndFat = abricotPredicate.and( fatPredicate );
Predicate
@FunctionalInterface public interface Function<T, R>{ R apply(T t); } public static <T, R> List<R> map(List<T> list, Function<T, R> f) { List<R> result = new ArrayList<>(); for(T s: list){ result.add(f.apply(s)); } return result; } List<Integer> l = map( Arrays.asList("qcon","em","sampa"), (String s) -> s.length() ); // [4, 2, 5]
Function
(Pug p1,Pug p2) -> "black".equals( pug.getColor() )
Parâmetros do Lambda
"Arrow" Corpo do Lambda
Código funcional em Java: Superando o hype
Eder Ignatowicz
Manipula coleções de forma declarativa
Quais são os nomes dos Pugs com peso maior do que 9 quilos?
Manipula coleções de forma declarativa
SELECT nome FROM pugs WHERE weight < 9 order by peso.
Em Java
List<Pug> gordinhos = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getWeight() > 9 ) { gordinhos.add( pug ); } } Collections.sort( gordinhos, new Comparator<Pug>() { @Override public int compare( Pug p1, Pug p2 ) { return Integer.compare( p1.getWeight(), p2.getWeight() ); } }); List<String> nomeGordinhos = new ArrayList<>(); for ( Pug pug : gordinhos ) { nomeGordinhos.add( pug.getNome() ); }
Em Java
List<String> gordinhosNome = pugs.stream() .filter( d -> d.getWeight() > 9 ) .sorted( comparing( Pug::getWeight ) ) .map( Pug::getNome ) .collect( toList() );
Seleciona > 9 kg Ordena por peso Extrai o nome Armazena em uma lista
Em Java
List<Pug> gordinhos = new ArrayList<>(); for ( Pug pug : pugs ) { if ( pug.getWeight() > 9 ) { gordinhos.add( pug ); } } Collections.sort( gordinhos, new Comparator<Pug>() { @Override public int compare( Pug p1, Pug p2 ) { return Integer.compare( p1.getWeight(), p2.getWeight() ); } }); List<String> nomeGordinhos = new ArrayList<>(); for ( Pug pug : gordinhos ) { nomeGordinhos.add( pug.getNome() ); }
Em Java
List<String> gordinhosNome = pugs.parallelStream() .filter( d -> d.getWeight() > 9 ) .sorted( comparing( Pug::getWeight ) ) .map( Pug::getNome ) .collect( toList() );
Seleciona > 9 kg Ordena por peso Extrai o nome Armazena em uma lista
Stream Pipelines
filter pugs -> —> sorted —> map —> collect lambda lambda lambda
Código: Declarativo Componentizável Paralelizável
Streams API
Sequência de elementos de uma fonte que suporta operações de processamento em seus dados
Streams
Sequência de elementos de uma fonte que suporta operações de processamento em seus dados
Streams
Sequência de elementos de uma fonte que suporta operações de processamento em seus dados
Streams
Sequência de elementos de uma fonte que suporta operações de processamento em seus dados
Streams
Streams
Uma fonte de dados para a query Uma cadeia de operações intermediárias (pipeline) Uma operação terminal que gera o resultado
public Venda( Vendedor vendedor, int ano, int valor ) { this.vendedor = vendedor; this.ano = ano; this.valor = valor; } public Vendedor( String nome, String cidade ) { this.nome = nome; this.cidade = cidade; }
Vendedor eder = new Vendedor("Eder", "Campinas"); Vendedor pedro = new Vendedor("Pedro", "Apucarana"); Vendedor luciano = new Vendedor("Luciano", "Piracicaba"); Vendedor junior = new Vendedor("Junior", "Londrina"); List<Venda> transactions = Arrays.asList( new Venda( eder, 2014, 100 ), new Venda( eder, 2013, 200 ), new Venda( pedro, 2014, 300 ), new Venda( luciano, 2012, 500 ), new Venda( luciano, 2012, 400 ), new Venda( junior, 2012, 500 ));
Quais são as vendas que fizemos em 2014? Ordenadas?
List<Venda> vendas2014 = transactions .stream()
List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 )
List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 ) .sorted( comparing( Venda::getValor ) )
List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 ) .sorted( comparing( Venda::getValor ) ) .collect( toList() );
List<Venda> vendas2014 = transactions .stream() .filter( venda -> venda.getAno() == 2014 ) .sorted( comparing( Venda::getValor ) ) .collect( toList() ); vendas2014.forEach(System.out::println);
Venda{vendedor=Vendedor{nome='Eder', cidade='Campinas'}, ano=2014, valor=100} Venda{vendedor=Vendedor{nome='Pedro', cidade='Apucarana'}, ano=2014, valor=300}
Em que cidades temos vendedores?
List<String> cidadesAtendidas = vendas.stream() .map( venda -> venda.getVendedor().getCidade() ) .distinct() .collect( toList() );
Campinas Apucarana Piracicaba Londrina
Qual foi a maior venda?
Optional<Integer> maiorVenda = vendas.stream() .map(Venda::getValor) .reduce( Integer::max ); maiorVenda.ifPresent( i -> System.out.println(i));
500
Total de vendas?
Optional<Integer> totalVendas = vendas.stream() .map(Venda::getValor) .reduce( Integer::sum ); totalVendas.ifPresent( i -> System.out.println(i));
2000
Quais são as vendas de cada vendedor? Ordenadas?
Map<Vendedor, List<Venda>> vendedorPorVendas = vendas.stream() .sorted( comparing( Venda::getValor ) ) .collect( groupingBy( Venda::getVendedor ) ); System.out.println(vendedorPorVendas);
{Vendedor{nome='Junior', cidade='Londrina'}=[Venda{vendedor=Vendedor{nome='Junior', cidade='Londrina'}, ano=2012, valor=500}], Vendedor{nome='Pedro', cidade='Apucarana'}=[Venda{vendedor=Vendedor{nome='Pedro', cidade='Apucarana'}, ano=2014, valor=300}], Vendedor{nome='Luciano', cidade='Piracicaba'} =[Venda{vendedor=Vendedor{nome='Luciano', cidade='Piracicaba'}, ano=2012, valor=400}, Venda{vendedor=Vendedor{nome='Luciano', cidade='Piracicaba'}, ano=2012, valor=500}], Vendedor{nome='Eder', cidade='Campinas'}=[Venda{vendedor=Vendedor{nome='Eder', cidade='Campinas'}, ano=2014, valor=100}, Venda{vendedor=Vendedor{nome='Eder', cidade='Campinas'}, ano=2013, valor=200}]}Stream Pipelines
filter pugs -> —> map —> collect lambda lambda
Stream Pipelines
intermediate pugs -> —> —> findFirst lambda lambda intermediate
Lazy Streams
List<Dog> dogs = Arrays.asList( new Dog( "Dora", true, 10, Dog.RACA.PUG ), new Dog( "Bento", true, 13, Dog.RACA.PUG ), new Dog( "Rex", false, 8, Dog.RACA.SRD ), new Dog( "Teté", false, 6, Dog.RACA.SRD ), new Dog( "Banzé", true, 7, Dog.RACA.SRD ), new Dog( "Rin-Tin-Tin", false, 15, Dog.RACA.PASTOR ) );
Qual o nome do primeiro SRD que pesa mais do que 5kg?
String nomePrimeiroSRDMaiorDoQue5Kg = dogs.stream() .filter( dog -> { System.out.println( "filter - " + dog.getNome() ); return dog.getRaca().equals( Dog.RACA.SRD) && dog.getPeso() > 5; }) .map( dog -> { System.out.println( "map - " + dog.getNome() ); return dog.getNome(); }) .findFirst() .get();
Eager Streams
Dora Bento Rex Teté Banzé Rin Tin tin —> SRD e peso> 5 filter map Rex Teté Banzé —> Nome "Rex" “Teté" “Banzé" —> —> Nome findFirst “Rex" —> —>
Lazy Stream
Dora Bento Rex Teté Banzé Rin Tin tin —> SRD e peso> 5 filter map Rex —> Nome “Rex" —> —> first() findFirst “Rex" —> —>
String nomePrimeiroSRDMaiorDoQue5Kg = dogs.stream() .filter( dog -> { System.out.println( "filter - " + dog.getNome() ); return dog.getRaca().equals( Dog.RACA.SRD) && dog.getPeso() > 5; }) .map( dog -> { System.out.println( "map - " + dog.getNome() ); return dog.getNome(); }) .findFirst() .get();
filter - Dora filter - Bento filter - Rex map - Rex Rex
List<Dog> dogs = Arrays.asList( new Dog( "Dora", true, 10, Dog.RACA.PUG ), new Dog( "Bento", true, 13, Dog.RACA.PUG ), new Dog( "Rex", false, 8, Dog.RACA.SRD ), new Dog( "Teté", false, 6, Dog.RACA.SRD ), new Dog( "Banzé", true, 7, Dog.RACA.SRD ), new Dog( "Rin-Tin-Tin", false, 15, Dog.RACA.PASTOR ) );Criar uma lista de números primos infinita
public static List<Integer> primes(final int fromNumber, final int count) { return Stream.iterate(primeAfter(fromNumber - 1), Primes::primeAfter) .limit(count) .collect(Collectors.<Integer>toList()); }
public static List<Integer> primes(final int fromNumber, final int count) { return Stream.iterate(primeAfter(fromNumber - 1), Primes::primeAfter) .limit(count) .collect(Collectors.<Integer>toList()); }
System.out.println("10 primos do 1: " + primes( 1, 10 )); System.out.println("10 primos do 1000: " + primes( 1000, 10 )); 10 primos do 1: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] 10 primos do 1000: [1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061]
public static Long fib( int n ) { if ( n < 2 ) { return new Long( n ); } else { return fib( n - 1 ) + fib( n - 2 ); } }
Pure Functions
In computer programming, a function may be described as a pure function if both these statements about the function hold: 1-) The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change as program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices (usually—see below. 2-) Evaluation of the result does not cause any semantically
Integer doubleValue(Integer x) { return x * 2; } Integer doubleValue(Integer x) { if (cache.containsKey(x)) { return cache.get(x); } else { Integer result = x * 2; cache.put(x, result) ; return result; } }
private Map<Integer, Integer> cache = new ConcurrentHashMap<>();
public Integer fib(int n) { if (n == 0 || n == 1) return n; Integer result = cache.get( n ); if (result == null) { synchronized (cache) { result = cache.get(n); if (result == null) { result = fib(n - 2) + fib(n - 1); cache.put(n, result); } } } return result; }
public class Fibonacci { private Map<Integer, Integer> cache = new ConcurrentHashMap<>(); public Long fib(int n) { if (n == 0 || n == 1) return n; return cache.computeIfAbsent(n, (k) -> fib(k - 2) + fib(k - 1)); } }
Function<Integer, Integer> f = x -> x * 2; Function<Integer, Integer> g = Memoizer.memoize(f);
public class Memoizer { public static <T, R> Function<T, R> memoize( Function<T, R> fn ) { Map<T, R> map = new ConcurrentHashMap<T, R>(); return ( t ) -> map.computeIfAbsent( t, fn ); } }
static Integer longCalculation(Integer x) { try { Thread.sleep(1_000); } catch (InterruptedException ignored) { } return x * 2; } static Function<Integer, Integer> loooongCalc = Memoizer::longCalculation; static Function<Integer, Integer> loooongCalcMemo =
Memoizer.memoize(loooongCalc);
loooongCalcMemo.apply( 30 ); 1005ms loooongCalcMemo.apply( 30 ); 0 ms
static Function<Integer, Long> fibonacci = Fibonacci::calc; static Function<Integer, Long> memoFib =
Memoizer.memoize( fibonacci );
memoFib.apply( 30 ); 832040 56ms memoFib.apply( 30 ); 832040 0 ms
Programar funcional em Java é uma mudança de paradigma
Java é multi-paradigma Imperativo, OO e Funcional
Escolha o melhor deles para o seu problema
Java 8, Lambdas e Streams trouxeram uma nova vida para o Java