Sets y Maps
Interfaz Set
Un conjunto, Set, implementa la interfaz Collection y
Interfaces Set y Map
Clase TreeSet<T>
TreeSet es una colección
genérica que se implementa
como un árbol de búsqueda
binaria AVL. Implementa la interfaz OrderedSet que extiende de la interfaz Set.
Garantiza que los elementos
pueden accesarse en orden
ascendente.
Clase TreeSet<T>
Un TreeSet debe almacenar
objetos cuya clase implemente
la interfaz Comparable, para
que, al agregarlos al TreeSet los datos mantengan un orden entre ellos.
Además, garantiza que no haya
datos repetidos al utilizar los
métodos equals y hashcode que
también deben ser
Clase TreeSet<T>
TreeSet<String> colorSet = new TreeSet<String>();
String[] strArr = {“red”, “cyan”, “green”, “black”, “red”, “pink”};
for (int i = 0; i < strArr.length; i++) colorSet.add(strArr[i]);
System.out.println(colorSet.size());
Boolean hasGreen = colorSet.contains(“green”); System.out.println(colorSet);
System.out.println(colorSet.first()); System.out.println(colorSet.last());
Clase TreeSet<T>
Iterator<String> setIter = colorSet.iterator(); String color;
while (setIter.hasNext()) { color = setIter.next();
If (color.charAt(0) == ‘c’ || color.charAt(0) == ‘p’) setIter.remove();
}
Aplicación: Spell Checker
Para implementar un
spelling checker
se puede
utilizar un Set.
Guardar en un TreeSet el diccionario de palabras
Para cada palabra en el documento a verificar, buscar
si se encuentra en el diccionario (contains())
Si se encuentra, continuar
En caso contrario solicitar al usuario la acción a tomar
(agregarla al diccionario, ignorar la palabra, corregirla).
spellChecker()
public static void spellChecker(String filename) {
TreeSet<String> dictionary = new TreeSet<String>(), misspelledWords = new TreeSet<String>();
Scanner dictFile = null, docFile = null, keyIn; try {
dictFile = new Scanner(new FileReader("dict.dat")); docFile = new Scanner(new FileReader(filename)); }
catch(FileNotFoundException fnfe) {
System.err.println("Cannot open a file"); System.exit(1);
Ma. de los Angeles Junco Rey
spellChecker() (cont…)
keyIn = new Scanner (System.in); String word;
String response;
while(dictFile.hasNext()) { word = dictFile.next(); dictionary.add(word); }
while(docFile.hasNext()) { word = docFile.next();
if (!dictionary.contains(word)) { System.out.println(word);
System.out.print(“ 'a'(add) 'i'(ignore) 'm'(misspelled)");
spellChecker() (cont…)
if (response.charAt(0) == 'a') dictionary.add(word);
else if (response.charAt(0) == 'm') misspelledWords.add(word);
} }
System.out.println("\nMisspelled words: " + misspelledWords);
}
Salida del spellChecker
Enter the document to spell check: spell.txt teh
'a'(add) 'i'(ignore) 'm'(misspelled) m contians
'a'(add) 'i'(ignore) 'm'(misspelled) m url
'a'(add) 'i'(ignore) 'm'(misspelled) a web-page
'a'(add) 'i'(ignore) 'm'(misspelled) i misspeled
'a'(add) 'i'(ignore) 'm'(misspelled) m email
'a'(add) 'i'(ignore) 'm'(misspelled) i adress
'a'(add) 'i'(ignore) 'm'(misspelled) m
Clase HashSet<T>
La clase HashSet no almacena los datos en un orden
específico dentro de una tabla, dado que dicho orden
va a depender de la función hash que se utilice.
Por ello, la clase de los objetos que se almacenan en
un HashSet DEBEN implementar tanto el método
equals y hashcode (recordemos que ambos métodos se heredan de Object)
La clase HashSet no utiliza compareTo para verificar si
un objeto es igual a otro
Funciones Hash de Java
Java provee una función general de hashing con el
método hashCode() de la superclase Object.
public int hashCode() { … }
El método hashCode() convierte la dirección interna del
objeto en un valor entero.
Esto tiene sus limitantes ya que dos objetos diferentes
normalmente tendrán valores hash diferentes, aún cuando tengan la misma información.
Por ello, DEBE sobreescribirse tanto la función
1. Verifica
hashcode()
Clase HashSet<T>
El tiempo de ejecución para las operaciones de
add, remove, contains y size es constante
Clase HashSet<T>
Tiempo de ejecución
Suponer que:
Lee un archivo con 25025 palabras ordenadas
aleatoriamente y las inserta en un TreeSet y en un HashSet.
Calcula el tiempo promedio que se necesita para
generar ambas estructuras.
Revuelve el contenido del arreglo y observa el tiempo
Tiempo de ejecución
Ma. de los Angeles Junco Rey
Run:
Number of words is 25025
Built TreeSet in 0.078 seconds Built HashSet in 0.047 seconds
TreeSet search time is 0.078 seconds HashSet search time is 0.016 seconds
Ejemplo uso Hashset<T>
// creando un objeto Hashset
HashSet<Integer> hset = new HashSet<Integer>();
// agregando datos en el Hashset
hset.add( new Integer( 6 ) ); hset.add( new Integer( 1 ) ); hset.add( new Integer( 4 ) ); hset.add( new Integer( 4 ) ); hset.add( new Integer( 5 ) ); hset.add( new Integer( 18 ) ); hset.add( new Integer( 93 ) );
// imprimiendo los elementos del Hashset
Ejemplo uso Hashset<T>
// Iterando a traves del HashSet
Iterator<Integer> it = hset.iterator(); while (it.hasNext()){
System.out.print ( it.next() + " "); }
// Tratando de agregar duplicados
boolean val = hset.add( new Integer( 18 ) ); if ( val != true )
System.out.println( "\nNo puede agregarse el 18" ); else {
System.out.println( "\nSe agrego el 18" );
Ejemplo uso Hashset<T>
// Consultando el total de datos del Hashset
System.out.println( "\n\nEl total de datos en el HashSet es: " + hset.size() );
// Consultando si existe o no un dato en el Hashset
if (hset.contains(8))
System.out.println("\nEl Hashset SI contiene el 8"); else
System.out.println("\nEl Hashset NO contiene el 8");
Ejercicios a revisar
HashSet1.java
PruebaTreeSet.java
Sets.java
Sugerencias para sobreescribir el
método hashcode()
Leer artículo
hashcode and equals.pdf
Forma en la que Java sobreescribe el método hashcode para
la clase String:
Ma. de los Angeles Junco Rey
Sugerencias para sobreescribir el
método hashcode()
Así, una forma sencilla y relativamente “buena” de
sobreescribir el método hashcode para un tipo de dato definido por el usuario (Alumno, Profesor, etc) es
convertir los atributos “llave” a String y utilizar el
método hashcode para la clase String previamente visto:
Como generar el String toma tiempo, una forma de
Sugerencias para sobreescribir el método
hashcode() de una clase Fruit
public class Fruit {
private String flavour; private String colour;
public boolean equals ( Object other ) { if ( other == null ) return false; else
if ( other instanceof Fruit )
return this.flavour.equals ( ((Fruit)other ).flavour ); else return false;
}
public int hashCode() {
return flavour.hashCode(); }
Sugerencias para sobreescribir el
método hashcode()
Como regla general, cada vez que necesites utilizar un
objeto como llave de un Map (Hashtable, HashMap,
HashSet, TreeMap) DEBES redefinir tanto el método
equals como el método hashCode de tal forma que
incluyan los mismos atributos que conforman la llave.
En el ejemplo anterior, se usa el atributo flavour tanto
Mapas
Interfaz Map
Un mapa,
map
, guarda elementos como pares
llave-valor
. En un par, el
primer
elemento es
la llave que identifica en forma única a un
elemento. El
segundo
elemento es el valor
asociado a la llave.
No se permiten llaves
Interfaz Map
interface MAP<K,V> (partial) ds.util
void clear()
Removes all mappings from this map.
boolean containsKey(Object key)
Returns true if this map contains a mapping for the specified key.
boolean isEmpty()
Returns true if this map contains no key-value mappings.
V remove(Object key)
Removes the mapping for this key from this map if present. Returns the
previous value associated with specified key, or null if there was no mapping for key.
int size()
Returns the number of key-value mappings in this map.
Access/Update Methods
K get(Object key)
Returns the value to which this map maps the specified key or null if the map contains no mapping for this key.
V put(K key, V value)
Clase Hashtable<K,V>
La clase genérica Hashtable<K,V> guarda elementos en una tabla Hash, que mapea llaves a valores.
NO Permite valores null como llaves y está sincronizada.
Sincronizada: los métodos de HashTable que acceden a datos (size, get, put,
isEmpty, clear, remove…) están sincronizados por lo que no require que el programador implemente código para controlar dichos accesos.
hashCode() debe ser implementado por la llave genérica.
equals() debe ser implementado por la llave genérica.
Una instancia de Hashtable cuenta con dos parámetros que afectan su desempeño:
Capacidad inicial
Factor de carga (0.75): qué tan llena debe estar la tabla ANTES de incrementar su tamaño.
Ejemplo de Hashtable
Hashtable<String, Integer> numbers
= new Hashtable<String, Integer> ( ); numbers.put("one", 1);
numbers.put("two", 2); numbers.put("three", 3);
Integer n = numbers.get("two"); if (n != null) {
Ejemplo de Hashtable<K,V>
Uso de una tabla hash
hash.java
La clase HashMap<K,V>
Un HashMap no se encuentra ordenado dado que la
posición de sus elementos depende de la evaluación de las llaves con la función hash.
El método toString() regresa la lista de los elementos en
el orden en el que los consulta el iterador.
HashTable NO permite llaves o valores null. HashMap
HashMap vs HashTable
HashTable es sincronizable, HashMap no. Así, para aplicaciones
que no requieren de múltiples hilos de ejecución se puede utilizar HashMap.
Como HashMap no está sincronizada su desempeño es mejor que
HashTable.
HashMap tiene como sublcase LinkedHashMap de tal manera que
puedas tener una forma de predecir el orden de iteración.
HashTable es obsoleta, por lo que es mejor utilizar
ConcurrentHashMap si se requiere manejar múltiples hilos y sincronización.
La clase HashMap<K,V>
Un HashMap guarda elementos en una
Constructores de
Hashmap(K,V)
La clase TreeMap<K,V>
TreeMap
es una colección ordenada que accesa
los elementos en forma ascendente de sus
llaves.
Implementa la interfaz OrderedMap que incluye
La clase TreeMap<K,V>
String[] className = {"ECON 101","CS 173","ENGL 25"}; int[] enrollment = {85,14, 30};
TreeMap<String, Integer> tm = new TreeMap<String, Integer>();
for(int i = 0; i < 3; i++)
tm.put(className[i], enrollment[i]);
La clase TreeMap<K,V>
System.out.println(“Alumnos en ECON 101 : “+
tm.get(“ECON 101”)); // 85
System.out.println(“El mapa contiene MATH 51 : “+
tm.containsKey(“MATH 51”)); // false
System.out.println(“Alumnos en el primer curso: “+
Colecciones Map
Un mapa no cuenta con acceso por medio de
iteradores.
Se cuenta con objetos llamados
“collection
views”
que son conjuntos (sets) que
implementan los métodos de la interfaz Set
pero se aplican sobre un mapa.
Colecciones Map
En la interfaz Map, el método
keySet
() regresa un
conjunto de llaves en el mapa. Esta “collection
view” implementa la interfaz Set.
Key Set Collection View
La vista
keySet
() es un conjunto de llaves en el
Entry Set Collection View
En la interfaz Map, existe una segunda vista
llamada:
entry set
. Consiste en el conjunto de
entradas llave-valor que es devuelta por el
método del mapa llamado
entrySet
().
Entry Set Collection View
interface MAP.ENTRY<K,V> ds.util.Map
K getKey()
Returns the key corresponding to this entry..
V getValue()
Returns the value corresponding to this entry..
V setValue(V value)
Replaces the value corresponding to this entry with the specified value. Returns the old value corresponding to the entry
Entry Set Iterators
TreeMap<String, Time24> confTimeMap =
new TreeMap<String, Time24>();
confTimeMap.put("Session 1", new Time24(9,30)); confTimeMap.put("Session 2", new Time24(14,00)); confTimeMap.put("Lunch", new Time24(12,0));
Entry Set Iterators …
Ma. de los Angeles Junco Rey
// declare an entry set for map confTimeMap
Set<Map.Entry<String,Time24>> entries = confTimeMap.entrySet();
// declare an iterator for the entry set using the Set iterator() // method
Entry Set Iterators …
Ahora se quiere retrasar todas las actividades
media hora.
// use a loop to scan the entries in the map while (iter.hasNext())
{
// extract the next element as a Map.Entry object Map.Entry<String, Time24> me = iter.next();
Time24 t = me.getValue(); t.addTime(30);
Entry Set Iterators …
Imprimir el horario de las sesiones, únicamente
Ma. de los Angeles Junco Rey
for (Map.Entry<String,Time24> i : entries) {
String activity = (String)i.getKey();
if (activity.indexOf("Session") != -1)
System.out.println("Activity " + activity + " Starting time " + i.getValue()); }
Output:
Ejemplo
Crea un HashMap<String, LinkedList<String>> donde la llave sea un estado y el valor sea una lista de ciudades de dicho estado.
Para cada dato que leas del archivo de estados y
ciudades, verifica:
Si el estado ya existe
Si la ciudad aún NO está en la lista, agrégala en la
lista de ciudades de dicho estado
Si la ciudad ya existe NO hagas nada
Si el estado NO existe
Crea un nuevo par de datos llave-valor con el estado
HashMap<String, LinkedList<String>> cities = new HashMap<String, LinkedList<String>>(); String key="",city="";
Scanner lector=null; try {
lector = new Scanner(new FileReader("EstadoCiudades.dat")) ; while(lector.hasNext()) {
key = lector.next(); city = lector.next();
Ma. de los Angeles Junco Rey
Crea HashMap de estado – lista de
if (!cities.containsKey(key)) {
LinkedList<String> nueva=new LinkedList<String>(); nueva.add(city);
cities.put(key,nueva); }
else {
LinkedList<String> lista = cities.get(key); if (!lista.contains(city))
lista.add(city); }
} // while } // try
Si la ciudad aún NO está en la lista, agrégala
El estado aun no existe en el mapa