Quando itero uma coleção usando o novo açúcar sintático do Java 8, como
myStream.forEach(item -> {
// do something useful
});
Isso não é equivalente ao snippet 'old syntax' abaixo?
myStream.forEach(new Consumer<Item>() {
@Override
public void accept(Item item) {
// do something useful
}
});
Isso significa que um novo Consumer
objeto anônimo é criado na pilha toda vez que eu itera sobre uma coleção? Quanto espaço de pilha isso leva? Que implicações de desempenho ele tem? Isso significa que devo preferir usar o estilo antigo para loops ao iterar sobre grandes estruturas de dados de vários níveis?
Respostas:
É equivalente, mas não idêntico. Simplificando, se uma expressão lambda não capturar valores, será um singleton que será reutilizado em todas as chamadas.
O comportamento não está exatamente especificado. A JVM recebe grande liberdade sobre como implementá-la. Atualmente, a JVM da Oracle cria (pelo menos) uma instância por expressão lambda (ou seja, não compartilha instância entre diferentes expressões idênticas), mas cria singletons para todas as expressões que não capturam valores.
Você pode ler esta resposta para obter mais detalhes. Lá, eu não apenas dei uma descrição mais detalhada, mas também testei o código para observar o comportamento atual.
Isso é coberto por The Java® Language Specification, capítulo “ 15.27.4. Avaliação em tempo de execução de expressões lambda ”
Resumido:
fonte
Quando uma instância representando o lambda é criada com sensibilidade, depende do conteúdo exato do corpo do seu lambda. Ou seja, o fator principal é o que o lambda captura do ambiente lexical. Se ele não capturar nenhum estado variável de criação para criação, uma instância não será criada toda vez que o loop for-for inserido. Em vez disso, um método sintético será gerado em tempo de compilação e o site de uso lambda receberá apenas um objeto singleton que delega para esse método.
Observe também que esse aspecto depende da implementação e você pode esperar aprimoramentos e avanços futuros no HotSpot em direção a uma maior eficiência. Existem planos gerais para, por exemplo, criar um objeto leve sem uma classe correspondente completa, que possui informações suficientes para encaminhar para um único método.
Aqui está um artigo detalhado, bom e acessível sobre o tópico:
http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
fonte
Você está passando uma nova instância para o
forEach
método Toda vez que você faz isso, cria um novo objeto, mas não um para cada iteração de loop. A iteração é feita dentro doforEach
método usando a mesma instância de objeto 'callback' até que seja feita com o loop.Portanto, a memória usada pelo loop não depende do tamanho da coleção.
Sim. Tem pequenas diferenças em um nível muito baixo, mas não acho que você deva se preocupar com elas. As expressões Lamba usam o recurso invokedynamic em vez de classes anônimas.
fonte