Qual é a vantagem do Iterator?

33 respostas
nadilsons

Bom dia,

Alguém sabe a vantagem de usar Iterator ao invés de um for normal para percorrer um List?

Obrigado,
Nadilson

33 Respostas

fabiozoroastro

Pra dizer que java é difícil…

:slight_smile:
Abraços.

T

Iterator é um padrão para percorrer listas, conjuntos, mapas etc.
É o que você chamaria de “cursor” se estivesse escrevendo stored procedures em algum banco SQL. Portanto não é um conceito novo ou extraordinariamente difícil de manipular.
Se você sabe que o seu List é um ArrayList, então não há problemas em usar o índice em vez de usar um Iterator. Para todos os outros tipos (LinkedList, Set, Map etc.) você tem de usar o Iterator.
E de qualquer maneira você continua a usar o for:

// digamos que coleção seja uma coleção de BlaBleBli
for (Iterator it = colecao.iterator(); it.hasNext(); ) {
     BlaBleBli obj = (BlaBleBli) it.next();
}

Em Java 5, o “iterator” pode até ficar escondido:

for (BlaBleBli obj : colecao) {
}
ramilani12

Por exemplo se vc usar uma LinkedList com objetos nao ordenados o for get(i) percorrerá toda lista para encontrar tal objeto qnto ao Iterator buscara esse objeto de forma mais rápida pulando de nó em nó …

Agora qnto ao ArrayList usando o iterator ou for nao sei se faz diferença…

nadilsons

Eu nao tinha pensado nesta possibilidade… sempre associei Iterator - while.

Quer dizer entao que o iterator melhora a performace para Collections nao ordenados… interessante.

Obrigado a todos pela colaborãção.

Nadilson

ViniGodoy

Outra vantagem é que, a menos que você tenha uma lista imodificável, o Iterator permite que você remova um objeto enquanto está percorrento a lista.

Por exemplo:

while (it.hasNext) { obj = it.next(); if (canExclude(obj)) it.remove(); }

Você tem a garantia que o Iterator não vai se perder nos índices. Ele também vai remover da melhor forma possível.

No caso do LinkedList, faz diferença usar o iterator. A cada get() a LinkedLIst vai até o primeiro nó e então pula de nó em nó em busca de um elemento. No caso do iterator, ele só “desloca o ponteiro”.

No caso do Java 5, você pode substituir o iterator pelo for each sem prejuízo. A menos é claro, que queira excluir elementos.

Bateramos

Tem também o esquema que o ArrayList e Vector implementão RandomAccess. Tornando mais rápido usar um for() do o Iterator. Mas se o ArrayList e Vector tiverem um tamanho meio que grande, eles tem acesso linear, dai é melhor usar um Iterator.

Só não saquei porque isso acontece???

ViniGodoy, o que esse “canExclude()” verificaria??? Não rola um ConcurrentModificationException quando você tenta excluir um index da Collection enquanto estiver na interação??? o que eu sempre faço é marcar esse cara para deleção, e assim que sair da interação, excluir ele por fora.

sergiotaborda

Bateramos:
Tem também o esquema que o ArrayList e Vector implementão RandomAccess. Tornando mais rápido usar um for() do o Iterator. Mas se o ArrayList e Vector tiverem um tamanho meio que grande, eles tem acesso linear, dai é melhor usar um Iterator.

Só não saquei porque isso acontece???

ViniGodoy, o que esse “canExclude()” verificaria??? Não rola um ConcurrentModificationException quando você tenta excluir um index da Collection enquanto estiver na interação??? o que eu sempre faço é marcar esse cara para deleção, e assim que sair da interação, excluir ele por fora.

Se usar um iterador não dá exceção. Essa é a vantagem. É mais eficiente e seguro que um mecanismo de marcação.
A função canExclude é uma função imaginária que verificaria se o elemento deve ser excluido.

Embora ArrayList implemente RandomAccess vc não deve colocar isso na assinatura dos seus métodos, deve ser usar apenas List.
Mas ai vc não sabe mais se implementa RandomAcces ou não, então é mais seguro usar o iterador, ou vc se arrisca a usar get(index) num LinkedList e ter a performance do seu método indo pelo cano. Ha poucas ocasiões onde vc precisa de um RandomAcess e é na ordenação com Collections.sort() , fora isso, não ha muitas razões para usar for sem iterador. Aliás, for sem iterator é um sinal de codigo feito por amadores/juniors e é preciso que esteja documentado corretamente se essa for a implementação necessária para o caso. Caso contrário é um erro de codificação.

xdraculax

Levem a mal a reabertura da thread não, mas é porque não entendi essa afirmação:

Porque?

Quer dizer que essa implementação é incorreta?

for(int i= 0; i<=x; i++) { 
     //... 
}
Bateramos

não é incorreta.
Para navegar por um array, levando em conta que X é o legth do array… tá de boa esse código, sussa!

Mas para Collection… tipo List ou Set, é melhor usar um iterator, que você consegue remover e ditar os elementos sem grandes problemas

sergiotaborda

xdraculax:
Levem a mal a reabertura da thread não, mas é porque não entendi essa afirmação:

Porque?

Quer dizer que essa implementação é incorreta?

for(int i= 0; i<=x; i++) { //... }

Dependendo do que está dentro do for sim.

se vc faz codigo assim:

List lista = .. .
for(int i= 0; i<=x; i++) { 
     lista.get(i);
}

Então sim. Isso é totalmente proibitivo se a lista for um LinkedList. ( É por isso que todo o mundo só usa arraylist com a desculpa que é mais eficiente fazer o for. Não é. O for com iterator do linkelist é mais eficiente - porque não usa nenhuma variável de controle)

No java 5 e depois , ha muito poucas razões para usar iteração com inteiros.
Mesmo com arrays vc pode usar o for estendido. E nesse caso o uso de iterator é apenas para quando quer usar o remove().

ViniGodoy

Não é mais eficiente pois a lista não representa uma estrutura contínua de memória, enquanto o ArrayList, sim. Entretanto, esse é o tipo de otimização de microcódigo que, a menos que você faça MUITAS operações em listas, ou use listas MUITO grandes, jamais notará uma diferença de performance significativa.

De qualquer forma, hoje em dia não há muitas razões para não usar o for each, como o próprio sergio ressaltou. A única que vejo é se você precisar excluir elementos no processo, o que já te obrigaria a usar um Iterator de um jeito ou de outro.

ViniGodoy
xdraculax:
Quer dizer que essa implementação é incorreta?

Só para explicar mais o que o Sergio falou.

Em linked lists o método get precisa obrigatóriamente percorrer a lista até o n-ézimo elemento. Podemos imaginar que a implementação dele segue o seguinte algoritmo:

public T get(int index) {
   Node<T> node = getFirst();
   for (int i = 0; i < index; i++) 
      node = node.next();

   return node.getValue();
}

Perceba que fazer o for usando o get irá disparar aquele for interno várias vezes. Já na implementação com o iterator isso não ocorre. O iterator provavelmente é implementado mais ou menos assim:

public class LinkedListIterator<T> implements Iterator<T> 
{
   private Node<T> node;
   private bool beforeFirst;

   private LinkedListIterator(Node<T> first) { 
      this.node = first; 
   }

   @override 
   public bool hasNext() { 
      return node != null; 
   };

   @override 
   public T next() { 
      if (beforeFirst) {
        beforeFirst = false;
        return node.getValue();
      }

      node = node.next();
      return node.getValue();
    }
}

Note que aqui, cada chamada ao next apenas avança um item da lista. Não existe um for inteiro, percorrendo a lista do zero até aquele item. Como muitas vezes você percorre a lista através da interface List, você não tem como garantir se a lista sendo percorrida é ou não um arraylist. Por isso, a iteração por coleções usando índice é inteiramente desaconselhada.

xdraculax

Então, se eu uso o for, e dentro do for eu acesso o elemento de uma LinkedList pelo índice, significa que na execução do get, a iteração vai percorrer todos os elementos da LinkedList até o índice da iteração atual do MEU loop, correto?

Estou perguntando porque já vi isso em uns códigos aqui, e não reparei nisso.

Quando vou fazer iteração com loop (se for um ArrayList) eu uso o:

for( Tipo t: coleção){…}

Nesse caso, ele use o iterator para percorrer os elementos da lista?

ViniGodoy

Sim, o for each usa o iterator automaticamente.

É isso mesmo, cada get que você dá num linked list, ele percorre do início até a posição do get. Isso dentro de um for transforma o método em algo de performance catrastrófica.

Para o ArrayList não existe esse problema, nem com índices, nem com o iterator, já que o ArrayList é implementado através de um array e um get pode simplesmente retornar aquela posição do vetor.

sergiotaborda

xdraculax:
Então, se eu uso o for, e dentro do for eu acesso o elemento de uma LinkedList pelo índice, significa que na execução do get, a iteração vai percorrer todos os elementos da LinkedList até o índice da iteração atual do MEU loop, correto?

Estou perguntando porque já vi isso em uns códigos aqui, e não reparei nisso.

Quando vou fazer iteração com loop (se for um ArrayList) eu uso o:

for( Tipo t: coleção){…}

Nesse caso, ele use o iterator para percorrer os elementos da lista?

Sim. Mas veja que na realidade o for extendido é uma operação especial do compilador.
Ele compila codigo diferente conforme o objeto iterado é um array ou um Iterable. qualquer Iterable pode ser usado, não apenas Collection e Map e suas filhas. Vc pode criar objetos que são naturalmente compostos e iterálos facilmente, exemplo

for (ItemPedido item : pedido ){

 
}

no caso aqu teriamos a classe Pedido que implementa Iterable

xdraculax

Entendi, obrigado pela ótima explicação :):):).

Só uma nota: então, as implementações de estruturas de dados como LinkedList não deveriam ter um método de obtenção por índice (como o get), e sim somente hasNext() e next().

ViniGodoy

E por que não? Você pode, efetivamente, querer pegar o segundo elemento da lista uma única vez:

public Car segundoLugar = finalistas.get(2);

Não é a operação mais rápida do mundo num LinkedList, mas é uma operação possível em listas. Se for feita esporadicamente, ou em listas menores, não compromete a performance.

Mas por isso, quando você vai utilizar collections, é importante conhecer bem as características de cada uma delas. Especialmente se você estiver trabalhando com otimização, seja de memória ou de velocidade.

A

Oi Thingol, por favor pode me explicar como assim todos os outros usam o iterator?

A

Oi Thingol/Vini, podem me explicar como assim todos usam o iterator?

abraço, :smiley:
André AS

rarylson

Se quiserem entender bem a vantagem de um Iterator, experimenta percorrer uma árvore da Collections em pré-ordem e depois em pós-ordem sem usar Iterator…

danilocmiranda

E adicionar um Objeto a coleção durante o for?
Como a gente faz isso com iterator?

ViniGodoy

danilocmiranda:
E adicionar um Objeto a coleção durante o for?
Como a gente faz isso com iterator?

Isso não tem como.

danilocmiranda

Então qual a melhor forma de percorrer uma List e adicionar um objeto a ele?
Só criando uma nova List com os novos objetos?

ViniGodoy

Não entendi. Pra que você precisa percorrer o ArrayList? Não basta só dar add?

Se você não puder inserir duplicados, então é melhor usar um Set. O set também cobre o caso para a ordenação.

Se você precisar inserir um objeto numa ordem específica, faça a busca com o Collections.binarySearch e em seguida use o método insert que aceita como parãmetro um índice, além do valor.

danilocmiranda

Eu tenho uma lista que é alimentada pelos objetos comuns de outras Listas (podem ser de duas a quatro listas)
Hoje o que faço é percorrer a List com o resultados dos objetos comuns das outras listas e dentro um for aninhado que percorre as outras listas e incrementa na lista Resultado.
Então dentro destes for’s vou incrementando numa Lista Auxiliar e fora do for eu “seto” esta lista auxiliar na lista resultado.

Mais ou menos assim

private List<ListFiltroVO> popularFiltros(List<ProdutoVO> listProd) {
		Integer contador = 0;
		ListFiltroVO tipoFiltroComFiltros = new ListFiltroVO();
		List<ListFiltroVO> listaFiltroVoAuxiliar = new ArrayList<ListFiltroVO>();
		if (listProd != null) {
			listFiltrosView = new ArrayList<ListFiltroVO>();
			for (ProdutoVO produto : listProd) {
				for (FiltroVO pv : produto.getFiltros()) {
					if (listFiltrosView.size() == 0) {
						tipoFiltroComFiltros.setDescricao(pv.getTipoFiltro()
								.getNomeTipoFiltro());
						tipoFiltroComFiltros.setListItens(getListFiltros());
						List<FiltroVO> listAux = new ArrayList<FiltroVO>();
						tipoFiltroComFiltros.setListItens(listAux);
						tipoFiltroComFiltros
								.setListaFiltros(listaFiltroVoAuxiliar);
						tipoFiltroComFiltros.getListItens().add(pv);
						tipoFiltroComFiltros.setContador(contador);
						listFiltrosView.add(tipoFiltroComFiltros);
						listaFiltroVoAuxiliar = listFiltrosView;
					} else {
						tipoFiltroComFiltros = new ListFiltroVO();
						tipoFiltroComFiltros.setDescricao(pv.getTipoFiltro()
								.getNomeTipoFiltro());
						if (!listaFiltroVoAuxiliar
								.contains(tipoFiltroComFiltros)) {
							List<FiltroVO> listAux = new ArrayList<FiltroVO>();
							tipoFiltroComFiltros.setListItens(listAux);
						
							tipoFiltroComFiltros.getListItens().add(pv);
							listFiltrosView.add(tipoFiltroComFiltros);
						} else {
							System.out.println("Este filtro ja existia");
						}
						if (!listFiltrosView.contains(tipoFiltroComFiltros)) {
							listFiltrosView.add(tipoFiltroComFiltros);
						}
					}

				}
			}
			return listFiltrosView;
		}
		return listFiltrosView;
	}

Está funcionando, mas estou achando complicado demais…
alguma dica para simplificar este código?

P.s.: Perdão se cometi algum pecado no código, estou me expondo para aprender. :wink:

ViniGodoy

O resultado final é o que tiver de comum em todas as listas, é isso?

danilocmiranda

Isso, o resultado é tudo que tem de comum entre as listas.
E ainda tenho que contar quantas vezes cada objeto apareceu nas listas.

ViniGodoy

Eu contaria através de um map<objeto, integer>().

danilocmiranda

Entendi, vou fazer isso, acho que colocando dentro do meu código vai ser bem simples.
Valeu pela dica Vini.
O que achou do código? Dá pra melhorar?

ViniGodoy

Aparentemente sim. Mas sem conhecer mais a fundo as classes e as regras de negócio, fica difícil afirmar com certeza.

danilocmiranda

Quanto a usar o HashMap para contar tenho uma dúvida.

Utilizo JSF 2 e não consigo exibir um HashMap diretamente do JSF (parece e a versão 2.1 vai fazer isso…)

Como posso depois desmembrar o HashMap em duas List ( List e List ) e depois exibir na tela em sincronia?

ViniGodoy

Use os métodos entrySet() e values() do HashMap.

R

Boa tarde,

estou fazendo a leitura de informações de uma planilha Excel através do apache poi

na minha planilha, tenho 28 colunas, porém quando vou acessar a 26ª coluna aparece o seguinte erro:

ArrayIndexOfBoundsException: 28

Se eu manipular a planilha, excluindo as ultimas 3 colunas, sendo a planilha com 25 colunas o erro não acontece.

Alguém pode por favor me ajudar com esta questão? Obrigado!

segue minha Classe de leitura de arquivo excel:

import java.beans.IntrospectionException;

import java.beans.PropertyDescriptor;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFCell;

import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;

import org.apache.poi.hssf.usermodel.HSSFRow;

import org.apache.poi.hssf.usermodel.HSSFSheet;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import org.apache.poi.hssf.util.CellReference;

import org.apache.poi.ss.usermodel.Cell;

import org.apache.poi.ss.usermodel.Row;

import com.hsbc.stivscvm.bean.STI_VO;

public class LeitorExcel {

public LeitorExcel()
{
}

@SuppressWarnings({ "deprecation", "unused" })
public static List<STI_VO> readExcelMovimentacao(String data) throws IOException, IllegalArgumentException, IntrospectionException, IllegalAccessException, InvocationTargetException { 
	
	List<STI_VO>listaSTI = new ArrayList<STI_VO>();
	HSSFWorkbook wb = null;
	HSSFRow row = null;
	HSSFCell cell = null;
	SimpleDateFormat sf =  new SimpleDateFormat("dd/MM/yyyy");


	File file = new File((new StringBuilder("H:\\Transfer\\Teste\\LeituraExcel\\Excel\\")).append(data).toString());
	File afile[] = file.listFiles();
	
	int i = 0;
	int count = 0;
	STI_VO sti = null;
	
	for (int j=0; i < afile.length; i++) {
		
		File arquivos = afile[i];
		
		InputStream inp = new FileInputStream(arquivos.getPath());
		wb = new HSSFWorkbook(inp);
		HSSFSheet sheet = wb.getSheetAt(0);
		
			int countline = 0;
		
			for (Iterator<Row> rit = sheet.rowIterator(); rit.hasNext();) {
				row = (HSSFRow) rit.next();

				countline++;
				if (countline > 2) {
						sti = new STI_VO();
						
						for (Iterator<Cell> cit = row.cellIterator(); cit.hasNext();) {
							cell = (HSSFCell) cit.next();
															
							if (cell.getCellType() != HSSFCell.CELL_TYPE_BLANK) {
									CellReference cellRef = new CellReference(row.getRowNum(), cell.getCellNum());
									String valor = null;
									if(cell==null){
							            System.out.println("Cell in Row number "+(cell.getRowIndex()+1)+" is null.");
							        }else{
										switch (cell.getCellType()) {
										
											case HSSFCell.CELL_TYPE_STRING: // '\001'
												System.out.println(cell.getStringCellValue());
						                        valor = cell.getStringCellValue();
						                        break;
						                    case HSSFCell.CELL_TYPE_NUMERIC: // '\0'
						                    	  valor = "" + cell.getNumericCellValue();
						                    	  System.out.println(cell.getNumericCellValue());
						                        break;
						                    case HSSFCell.CELL_TYPE_BOOLEAN:
						                    		System.out.println(cell.getBooleanCellValue());
												break;
						                	case HSSFCell.CELL_TYPE_FORMULA:
												System.out.println(cell.getCellFormula());
												break;
						                	case HSSFCell.CELL_TYPE_BLANK:
						                		System.out.println("Celula em Branco");
						                		continue;
											default:
										}
									}
									sti = addCellSti(cell.getCellNum(), valor.trim(), (new STI_VO()).getClass(), sti);
							}	
						}
						sti.setDtCota(sti.getDtCota().replace("00:00:00", ""));
			            sti.setDtCota(sti.getDtCota().trim());
			            if(!sti.getValorPL().contains("cota"))
			            {
			                 System.out.println(sti.getValorPL());
			                 listaSTI.add(sti);
			                 count++;
			                 System.out.println("linha: "+count);
			                 mapSTI_VO.put(sti.getValorPL(), sti);
			            }
			}
		}
		listaSTI.add(sti);
	}
	System.out.println("--- FIM DE ARQUIVO ---");
	return listaSTI;
}

public static STI_VO addCellSti(int i, String valor, Class<?> cotas, STI_VO instancia) 
		throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException 
{
	Field[] campos = cotas.getDeclaredFields();
	
	PropertyDescriptor descriptor = new PropertyDescriptor(	campos[i].getName(), cotas);
	if(i == 5 && valor.startsWith("0"))
        valor = valor.substring(1, valor.length());
		
        Method method = descriptor.getWriteMethod();
        method.invoke(instancia, new Object[] {
            valor.trim()
    });
        
    return instancia;
}
public static HashMap<String, STI_VO> mapSTI_VO = new HashMap<String, STI_VO>();

}

Criado 13 de dezembro de 2006
Ultima resposta 19 de mar. de 2014
Respostas 33
Participantes 12