Observação:
vários dos programas desta parte do material quando compilados irão resultar em
um “aviso”:
C:\>javac
sesnputil.java
Note: sesnputil.java uses
unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Trata-se do
seguinte problema: nas edições Java 1.4 e anteriores as coleções e iteradores
utilizavam a classe Object como tipo dos elementos que eram colecionados. A
partir da edição Java 1.5 passou a existir na linguagem Java o suporte a “generics”.
As classes relacionadas a coleções e
iteradores foram reescritas para poder utilizar “generics” (ao invés de usar de
maneira indiscriminada o tipo Object). Fica como tarefa de estudo para as férias o
estudo e uso das classes de coleção e iteração das versões com suporte de “generics”:
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
O ambiente
Java (pacote java.util) dá suporte a diferentes implementações de coleções (Collection).
Considere o
programa abaixo:
import java.util.*;
class sesnputil{
public static void main(String[]
arg){
Collection nd = new LinkedList();
nd.add(new Integer(123));
nd.add(new Integer(456));
nd.add(new Integer(789));
nd.add(new Integer(147));
System.out.println("adicionou "+nd.size()+" itens");
for(;nd.size()>0;){
Iterator it= nd.iterator();
for(;it.hasNext();){
System.out.println(it.next());
}
it.remove();
System.out.println("removeu ultimo. Tem "+nd.size()+"
itens");
}
}
}
Collection e Iterator são
interfaces e existem várias implementações destas interfaces no ambiente Java.
Vamos discutir de maneira simples e breve a hierarquia de interfaces e
hierarquia de classes no pacote java.util (1.3)
List, Set e SortedSet são
subinterfaces da interface Collection. List descreve o comportamento de
seqüências ou listas. Listas tipicamente permitem elementos duplicados. A
iteração sobre elementos de Collection é feita utilizando a interface Iterator
enquanto que a iteração sobre elementos de uma List é feita utilizando a
interface ListIterator. Set descreve o comportamento de conjuntos ou coleções
que não permitem elementos duplicados. SortedSet descreve o comportamento de
conjuntos cujos iteradores garantem alguma ordem. A iteração sobre elementos de
Set e SortedSet é feita utilizando a interface Iterator.
Nenhuma classe implementa
diretamente e na totalidade a interface Collection. Mas existe uma classe
abstrata que a implementa: AbstractCollection. A interface List é implementada
pelas classes LinkedList, ArrayList e Vector. A interface Set é implementada
pela classe HashSet. A interface SortedSet é implementada pela classe TreeSet.
No programa acima utilizamos uma
coleção do tipo Collection implementada pela classe LinkedList.
Exercícos
1.
Mudar
o programa acima para utilizar a implementação dada pela classe ArrayList.
2.
Mudar
o programa acima para utilizar uma coleção do tipo List (ao invés de
Collection).
3.
Mudar
o programa acima para utilizar uma coleção do tipo Set (ao invés de
Collection).
4.
Faça
um programa que verifica que a classe HashSet não aceita a adição (add) de
elementos duplicados.
O restante do material abaixo discute implementações bem simples mas
similares às implementações LinkedList e ArrayList. Observe que o código do
método main é o mesmo em cada uma das implementações, exceto pela classe que
implementa a interface Icolecao:
Icolecao nd = new
ListaEncadeada();
Icolecao nd = new
ListaArranjo();
Icolecao nd = new
ListaEncadeadaNP();
Icolecao nd = new
ListaEncadeadaDE();
As dimensões de implementação são
muitas; usar ou não usar arranjos, usar ou não usar encadeamento, usar ou não
usar nodos postiços, usar ou não usar encadeamento simples ou duplo, usar ou
não usar valores primitivos ou somente referências (Object) etc.
Para simplificar ainda mais
utilizamos as interfaces Icolecao e Iiterador ao invés de utilizar as
interfaces Collection e Iterator do pacote java.util, além disso iremos
trabalhar com valores do tipo int ao invés de trabalhar com referências
para instâncias da classe Object (como acontece nas implementações de
java.util).
A implementação abaixo é similar à
implementação LinkedList.
import java.util.NoSuchElementException;
interface Iiterador{
boolean temProximo();
int proximo();
void remove();
}
interface Icolecao{
boolean adicione(int i);
Iiterador iterador();
void limpa();
boolean estaVazia();
int tamanho();
}
class ListaEncadeada implements Icolecao{
class IteradorLE implements
Iiterador{
Cnodo curr=inicio;
Cnodo refAnt=null;
public boolean temProximo(){
return curr!=null;
}
public int proximo(){
if(inicio==null|curr==null)
throw new NoSuchElementException();
refAnt=curr; /* marca nodo a
ser removido */
curr=curr.prox;
return refAnt.info;
}
public void remove(){
if(inicio==null|refAnt==null)
throw new IllegalStateException();
cntElem--;
if(inicio==fim){
inicio=fim=refAnt=null;
return;
}
if(refAnt==inicio){
inicio=inicio.prox;
refAnt=null;
return;
}
Cnodo a;
for(a=inicio; a.prox!=refAnt;
a=a.prox);
a.prox=a.prox.prox;
if(refAnt==fim){
fim=a;
}
refAnt=null;
}
}
class Cnodo{int info; Cnodo prox;}
private Cnodo inicio=null;
private Cnodo fim=null;
private int cntElem=0;
public boolean adicione(int i){
Cnodo n=new Cnodo();
n.info=i;
n.prox=null;
if(inicio==null){
inicio=fim=n;
}else{
fim.prox=n;
fim=n;
}
cntElem++;
return true;
}
public void limpa(){
cntElem=0;
inicio=fim=null;
}
public boolean estaVazia(){
return cntElem==0;
}
public int tamanho(){
return cntElem;
}
public Iiterador iterador(){
return new IteradorLE();
}
}
class sesnp{
public static void main(String[]
arg){
/* cria colecao de inteiros
referida por nd /
Icolecao nd = new
ListaEncadeada();
nd.adicione(123);
nd.adicione(456);
nd.adicione(789);
nd.adicione(147);
System.out.println("adicionou "+nd.tamanho()+" valores
inteiros");
for(;nd.tamanho()>0;){
Iiterador it=nd.iterador();
for(;it.temProximo();){
System.out.println(it.proximo());
}
it.remove(); /* remove inteiro retornado por
proximo() */
System.out.println("removeu ultimo. Tem "+nd.tamanho()+"
valores inteiros");
}
}
}
A
implementação acima utiliza uma técnica conhecida como encadeamento de nodos.
Observe a figura abaixo.

Esta figura mostra uma coleção,
implementada com encadeamento de nodos, em três diferentes instantes. No
primeiro instante a coleção se encontra vazia. No segundo instante a coleção
tem um elemento (o valor inteiro 123) e no terceiro instante foi adicionado o
valor inteiro 456 à coleção.
O programa abaixo mostra uma outra
implementação das interfaces Icolecao e Iiterador, desta vez uma implementação
similar à ArrayList ou a Vector.
import java.util.*;
interface Iiterador{
boolean temProximo();
int proximo();
void remove();
}
interface Icolecao{
boolean
adicione(int i);
Iiterador iterador();
void limpa();
boolean estaVazia();
int tamanho();
}
class ListaArranjo implements
Icolecao{
class IteradorLA
implements Iiterador{
int
curr=0;
int
indRemove=-1;
public boolean temProximo(){
return curr<cntElem;
}
public int proximo(){
if(curr>=cntElem)throw
new NoSuchElementException();
indRemove=curr;
return rep[curr++];
}
public void
remove(){
if(indRemove<0)throw new IllegalStateException();
cntElem--;
curr--;
for(int i=indRemove;
i<=cntElem; i++) rep[i]=rep[i+1];
indRemove=-1;
}
}
private int tamanho=100;
int[] rep=new int[tamanho];
int
cntElem=0;
public boolean adicione(int i){
if(cntElem>=rep.length)throw new RuntimeException("arranjo estorou!");
rep[cntElem++]=i;
return true;
}
public void limpa(){
cntElem=0;
}
public boolean estaVazia(){
return cntElem==0;
}
public int tamanho(){
return cntElem;
}
public Iiterador iterador(){
return new IteradorLA();
}
}
class colecaoarray{
public static void
main(String[] arg){
Icolecao nd = new ListaArranjo();
nd.adicione(123);
nd.adicione(456);
nd.adicione(789);
nd.adicione(147);
System.out.println("adicionou
"+nd.tamanho()+" itens");
for(;nd.tamanho()>0;){
Iiterador it=nd.iterador();
System.out.println(it.proximo());
it.remove();
for(;it.temProximo();){
System.out.println(it.proximo());
}
System.out.println("removeu. Tem
"+nd.tamanho()+" itens");
}
}
}
A implementação abaixo é similar à
LinkedList, ou seja similar a uma implementação já mostrado acima. Mas observe
o uso de “Nodos Postiços” (“dummy nodes”). Com o uso de nodos postiços o código
para inserção e remoção de nodos fica um pouco mais simples. O uso de nodos
postiços é um aspecto de pouca importância mas é uma técnica difundida.
Exercício:
1.
Faça o
desenho, conforme convenções da disciplina, da coleção vazia.
2.
Faça o
desenho, conforme convenções da disciplina, da coleção com um elemento.
3.
Faça o
desenho da coleção com dois elementos.
4.
Faça o
desenho da coleção com três elementos e com o iterador referenciado por it logo
após sua criação.
import java.util.NoSuchElementException;
interface Iiterador{
boolean
temProximo();
int
proximo();
void remove();
}
interface Icolecao{
boolean
adicione(int i);
Iiterador iterador();
void limpa();
boolean estaVazia();
int tamanho();
}
class ListaEncadeadaNP
implements Icolecao{
class IteradorLENP implements Iiterador{
Cnodo curr=inicio.prox;
Cnodo refAnt=null;
public boolean
temProximo(){
return curr!=fim;
}
public int proximo(){
if(curr==fim) throw new NoSuchElementException();
refAnt=curr; /* marca nodo que pode ser removido */
curr=curr.prox;
return refAnt.info;
}
public void
remove(){
if(refAnt==null) throw new IllegalStateException();
cntElem--;
Cnodo a;
for(a=inicio; a.prox!=refAnt; a=a.prox);
a.prox=a.prox.prox;
refAnt=null;
}
}
class Cnodo{int info; Cnodo prox;}
private Cnodo inicio;
private Cnodo fim;
private int cntElem;
ListaEncadeadaNP(){
inicio=new Cnodo();
fim=new Cnodo();
inicio.prox=fim;
fim.prox=null;
cntElem=0;
}
public boolean adicione(int i){
Cnodo n=new Cnodo();
n.info=i;
n.prox=inicio.prox;
inicio.prox=n;
cntElem++;
return true;
}
public void limpa(){
cntElem=0;
inicio.prox=fim;
}
public boolean estaVazia(){
return cntElem==0;
}
public int tamanho(){
return cntElem;
}
public Iiterador iterador(){
return new IteradorLENP();
}
}
class senp{
public static void
main(String[] arg){
Icolecao nd = new
ListaEncadeadaNP();
nd.adicione(123);
nd.adicione(456);
nd.adicione(789);
nd.adicione(147);
System.out.println("adicionou
"+nd.tamanho()+" itens");
for(;nd.tamanho()>0;){
Iiterador it=nd.iterador();
for(;it.temProximo();){
System.out.println(it.proximo());
}
it.remove();
System.out.println("removeu ultimo. Tem "+nd.tamanho()+"
itens");
}
}
}
A implementação abaixo utiliza o
encadeamento duplo. A classe LinkedList na verdade usa é o encadeamento duplo! Além
do encademento duplo é utilizado um nodo postiço e é feito um encadeamento
“circular”.
import java.util.NoSuchElementException;
interface Iiterador{
boolean
temProximo();
int
proximo();
void remove();
}
interface Icolecao{
boolean
adicione(int i);
Iiterador iterador();
void limpa();
boolean estaVazia();
int tamanho();
}
class ListaEncadeadaDE
implements Icolecao{
class IteradorLDE implements Iiterador{
Cnodo curr=extr.prox;
Cnodo refAnt=null;
public boolean
temProximo(){
return curr!=extr;
}
public int proximo(){
if(curr==extr) throw new NoSuchElementException();
refAnt=curr;
curr=curr.prox;
return refAnt.info;
}
public void
remove(){
if(refAnt==null) throw new NoSuchElementException();
cntElem--;
refAnt.prox.ant=refAnt.ant;
refAnt.ant.prox=refAnt.prox;
refAnt=null;
}
}
class Cnodo{int info; Cnodo ant; Cnodo prox;}
private Cnodo extr;
private int cntElem=0;
ListaEncadeadaDE(){
Cnodo nodoPostico=new Cnodo();
nodoPostico.ant=nodoPostico;
nodoPostico.prox=nodoPostico;
extr=nodoPostico;
}
public boolean adicione(int i){
cntElem++;
Cnodo n=new Cnodo();
n.info=i;
n.ant=extr;
n.prox=extr.prox;
extr.prox.ant=n;
extr.prox=n;
return true;
}
public void limpa(){
cntElem=0;
extr.ant=extr;
extr.prox=extr;
}
public boolean estaVazia(){
return cntElem==0;
}
public int tamanho(){
return cntElem;
}
public Iiterador iterador(){
return new IteradorLDE();
}
}
class denp{
public static void
main(String[] arg){
Icolecao nd = new ListaEncadeadaDE();
nd.adicione(123);
nd.adicione(456);
nd.adicione(789);
nd.adicione(147);
System.out.println("adicionou
"+nd.tamanho()+" itens");
for(;nd.tamanho()>0;){
Iiterador it=nd.iterador();
for(;it.temProximo();){
System.out.println(it.proximo());
}
it.remove();
System.out.println("removeu ultimo.
Tem "+nd.tamanho()+" itens");
}
}
}