Java 8: Stream API – Parte 1, a motivação

17_1

Umas das novidades que entraram no Java 8, foi o stream, com esse recurso é possível realizar interações de maneira bastante fácil com as suas coleções. O primeiro item a ser explorado do Stream é a sua motivação, com isso será criado um exemplo prático com um modelo simples, um time de futebol.

public class Team {
private List players;
…
}

public class Player {

private String name;

private int gols;

private Position position;

...
}

public enum Position {
GOALKEEPER,DEFENDER, FORWARDS
}

Com isso, será realizado algumas manipulações dos jogadores, inicialmente o código será utilizando o Java 7. Como primeiro método, teremos um que retorne apenas os atacantes.

public List getForwarders() {
List forwarders = new ArrayList<>();
for (Player player : players) {
if(player.isFowarder()) {
forwarders.add(player);
}
}
return Collections.unmodifiableList(forwarders);
}

Seguindo esse raciocínio, o próximo método será um que retorne apenas os defensores.

public List getDefenders() {
List defenders = new ArrayList<>();
for (Player player : players) {
if(player.isDefender()) {
defenders.add(player);
}
}
return Collections.unmodifiableList(defenders);
}

Ao analisar o código existe uma forte semelhança entre os dois códigos, na verdade existe uma única diferença: a condição para adicionar o jogador na lista. Possivelmente os próximos métodos seguirão o mesmo raciocínio. Assim, será refatorado esse código criando uma interface Filtro.

public interface PlayerFilter {
boolean filter(Player player);
}

Com a refatoração, utilizando o PlayerFilter se obtém o seguinte código:

public List getForwarders() {
return getFilteredPlayers(new PlayerFilter() {
@Override
public boolean filter(Player player) {
return player.isFowarder();
}
});
}

public List getDefenders() {

return getFilteredPlayers(new PlayerFilter() {
@Override
public boolean filter(Player player) {
return player.isDefender();
}
});
}

private List getFilteredPlayers(PlayerFilter filter) {

List filteredPlayers = new ArrayList<>();
for (Player player : players) {
if(filter.filter(player)) {
filteredPlayers.add(player);
}
}
return Collections.unmodifiableList(filteredPlayers);

}

Ainda é possível realizar mais uma refatoração sem utilizar o Stream, é possível reparar que o código desejável é a lógica dentro das implementações de PlayerFilter, assim com lambda é possível deixar o código ainda mais legível.

public List getForwarders() {
return getFilteredPlayers(player -> player.isFowarder());
}

public List getDefenders() {
return getFilteredPlayers(player -> player.isDefender());
}

Se pode deixar ainda mais simples utilizando o method reference. Da seguinte forma:

public List getForwarders() {
return getFilteredPlayers(Player::isFowarder);
}

public List getDefenders() {
return getFilteredPlayers(Player::isDefender);
}

Será que ninguém pensou nisso antes? A API de Stream é uma nova estrutura de dados que nasceu no Java 8 para trabalhar com fluxos de dados de modo sequencial ou paralelo de maneira muito fácil e intuitiva. De maneira geral segue algumas vantagens dessa API:

  • Maneira fácil e intuitiva para manipular sequencia de dados (Sequencial ou paralelo).
  • A Stream não impacta a lista já existente, assim toda operação de filtragem, por exemplo, não removerá elementos da lista original.
  • Um Stream só será executado quando tiver um método que encerra o pipeline[1].

 

Tão logo é criado o Stream e realizado a filtragem, o próximo passo é retornar as informações, para fazer isso é esperado a interface Collector, um padrão muito comum no Java é criar uma classe utilitária de interface apenas acrescentando o ‘s‘, assim a classe utilitária de <i>Collector</i> é <i>Collectors</i> tem alguns métodos que são muito úteis.

public List getForwarders() {
return players.stream().filter(Player::isFowarder).collect(Collectors.toList());
}

public List getDefenders() {
return players.stream().filter(Player::isDefender).collect(Collectors.toList());
}

Um ponto é que mesmo criando uma nova lista, talvez não seja uma boa ideia deixar o usuário da API modificar a lista oriunda da classe Team, ou seja, queremos deixar a lista apenas como read-only, exatamente como estava como código em Java. Um método que existe dentro do Collectors para resolver isso é o “collectingAndThen”. No nosso caso, será passado a conversão para a lista e em seguida utilizaremos o mesmo método para a deixar-lha imodificável.

<br />public List getForwarders() {

return players.stream().filter(Player::isFowarder).collect(Collectors.
collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
}

public List getDefenders() {
return players.stream().filter(Player::isDefender).collect(Collectors.
collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
}

Com isso, foi discutido de forma resumida a motivação de utilizar o Stream API, para manter os posts de forma curto, mais detalhes sobre a API serão feitas num próximo post.

public class Player {

private String name;

private int gols;

private Position position;

public Player(String name, int gols, Position position) {
this.name = name;
this.gols = gols;
this.position = position;
}

public String getName() {
return name;
}

public int getGols() {
return gols;
}

public Position getPosition() {
return position;
}

public boolean isFowarder() {
return FORWARDER.equals(position);
}

public boolean isDefender() {
return DEFENDER.equals(position);
}

}
public class Team {

private List players;

public Team(List players) {
this.players = players;
}

public List getForwarders() {

return players.stream().filter(Player::isFowarder).collect(Collectors.
collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
}

public List getDefenders() {
return players.stream().filter(Player::isDefender).collect(Collectors.
collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
}

}
public class App
{
public static void main( String[] args )
{
List players = new ArrayList<>();
players.add(new Player("Bobo", 10, Position.FORWARDER));
players.add(new Player("Zé Carlos", 8, Position.FORWARDER));
players.add(new Player("Lima", 4, Position.DEFENDER));
players.add(new Player("Lima", 14, Position.DEFENDER));
players.add(new Player("Ronaldo", 1, Position.GOALKEEPER));
Team bahia = new Team(players);
List defenders = bahia.getDefenders();
List forwarders = bahia.getForwarders();
}
}
public enum Position {
GOALKEEPER,DEFENDER, FORWARDS
}

Referência:

Anúncios

Um comentário sobre “Java 8: Stream API – Parte 1, a motivação

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s