Não trabalho muito frequentemente com iteradores Java / C # diretamente, mas quando faço isso sempre me pergunto qual foi o motivo para projetar iteradores da maneira "próxima".
Para iniciar, é necessário mover o iterador, a fim de verificar se há alguns dados, você deve verificar se há o próximo elemento.
Um conceito mais atraente para mim é esse iterador - ele "começa" desde o início e você pode verificar esse estado, digamos isValid
. Portanto, o loop sobre a coleção inteira ficaria assim:
while (iter.isValid())
{
println(iter.current());
iter.next();
}
Como você está aqui para verificar se existe o próximo elemento, você vai para lá e depois verifica se o estado é válido.
YMMV, mas enfim - existe alguma vantagem do próximo iterador (como em Java / C #) sobre esse iterador?
Nota: esta é uma questão conceitual, sobre o design da linguagem.
Respostas:
Penso que uma vantagem do modelo C # / .NET sobre o
MoveNext()
modelo JavahasNext()
é que o primeiro implica que algum trabalho possa ser feito. OhasNext()
método implica uma simples verificação de estado. Mas e se você estiver iterando um fluxo lento e precisar acessar um banco de dados para determinar se há resultados? Nesse caso, a primeira chamada parahasNext()
pode ser uma operação longa de bloqueio. A nomeação dohasNext()
método pode ser muito enganadora quanto ao que realmente está acontecendo.Para um exemplo do mundo real, esse problema de design me incomodou ao implementar as APIs de Extensões Reativas (Rx) da Microsoft em Java. Especificamente, para que o iterador criado por
IObservable.asIterable()
funcione corretamente, ohasNext()
método precisará bloquear e aguardar a chegada da próxima notificação de item / erro / conclusão. Isso não é intuitivo: o nome implica uma simples verificação de estado. Compare isso ao design do C #, ondeMoveNext()
seria necessário bloquear, o que não é um resultado totalmente inesperado quando você está lidando com um fluxo avaliado preguiçosamente.O que quero dizer é o seguinte: o modelo "next-iterator" é preferível ao modelo "this-iterator" porque o modelo "this-iterator" é frequentemente uma mentira: você pode ser solicitado a buscar previamente o próximo elemento para verificar o estado do iterador. Um desenho que comunique claramente essa possibilidade é preferível, na minha opinião. Sim, as convenções de nomenclatura de Java seguem o modelo "próximo", mas as implicações comportamentais são semelhantes às do modelo "this-iterator".
Esclarecimento: Talvez contrastar Java e C # fosse uma péssima escolha, porque o modelo de Java é enganoso. Considero que a distinção mais importante nos modelos apresentados pelo OP é que o modelo "this-iterator" desacopla a lógica "é válida" da lógica "recuperar atual / próximo elemento", enquanto uma implementação ideal de "próximo iterador" combina essas operações. Eu acho que é apropriado combiná-los porque, em alguns casos, determinar se o estado do iterador é válido requer a pré-busca do próximo elemento , para que a possibilidade seja tornada o mais explícita possível.
Não vejo uma diferença significativa de design entre:
Mas vejo uma diferença significativa aqui:
Nos exemplos
isValid()
ehasNext()
, a operação implícita como uma verificação de estado rápida e sem bloqueio pode ser de fato uma operação lenta e de bloqueio . NomoveNext()
exemplo, a maior parte do trabalho está sendo realizada no método que você esperaria, independentemente de estar lidando com um fluxo avaliado avidamente ou preguiçosamente.fonte
hasNext()
é semelhante aisValid()
, enext()
é semelhante aocurrent()
.Se houver algum cálculo que precise ser feito para calcular cada valor, antes de solicitar o primeiro valor, antes de solicitar o primeiro valor, você poderá adiar o processamento desse primeiro valor além da construção do iterador.
Por exemplo, o iterador pode representar uma consulta que ainda não foi executada. Quando o primeiro item é solicitado, o banco de dados é consultado para obter os resultados. Usando o design proposto, esse cálculo precisaria ser feito na construção do iterador (o que torna mais difícil adiar a execução) ou ao chamar
IsValid
, nesse caso, é realmente um nome impróprio, pois a chamadaIsValid
realmente obteria o próximo valor.fonte
hasNext
, apenas seu estado sobre o estado atual, não o futuro. Ou, adotando outra visão - compare isso com os iteradores do C ++, verifique se o estado é válido(current!=end)
sem tocar nos dados reais.hasNext
no início, aqui a consulta é executada, os dados são recuperados. No segundo modelo, eu chamoisValid
, aqui a consulta é executada. Portanto, o ponto da execução da consulta é o mesmo.IsValid
que você está realmente fazendo, vai obter o próximo valor. Isso significa que ele está fazendo exatamente a mesma coisa que o iterador existente (ele não possui a corrente válida desde o início, sem a necessidade de chamar um método para obtê-lo), mas apenas tem um nome incorreto.