12. Colección (3)

Resumen del capítulo

  • Iteradores
    • ListaIterador
  • Lista enlazada
  • apilarapilar

Iteradores

En cualquier colección debe haber alguna forma de insertar elementos y recuperarlos. Después de todo, preservar cosas es el trabajo más básico de una colección. Para List , add()es una forma de insertar elementos y get()una forma de obtener elementos.

Si lo piensas desde una perspectiva de alto nivel, hay un inconveniente: para usar una colección, tienes que programar el tipo exacto de colección. Puede que esto no parezca tan malo al principio, pero considere la siguiente situación: ¿Qué sucede si originalmente codificó una Lista , pero luego descubrió que sería más conveniente poder aplicar el mismo código a un Conjunto ? O supongamos que desea escribir un fragmento de código genérico desde el principio que no sabe ni le importa con qué tipo de colección está trabajando, por lo que puede usarse para diferentes tipos de colecciones, entonces, ¿cómo puede aplicarlo a diferentes tipos de colecciones sin reescribir el código?

El concepto de iteradores (también un patrón de diseño) permite esta abstracción. Un iterador es un objeto que se mueve a través de una secuencia y selecciona cada objeto en la secuencia, sin que el programador cliente conozca o se preocupe por la estructura subyacente de la secuencia. Además, un iterador a menudo se denomina _objeto liviano_ (objeto liviano): es económico de crear. Debido a esto, es común ver algunas restricciones extrañas en los iteradores. Por ejemplo, el iterador de Java solo puede moverse en una dirección. Este iterador sólo se puede utilizar para:

  1. Utilice iterator()el método para pedirle a la colección que devuelva un Iterador . Iterator estará listo para devolver el primer elemento de la secuencia.
  2. Utilice next()el método para obtener el siguiente elemento de la secuencia.
  3. Utilice hasNext()el método para comprobar si todavía hay elementos en la secuencia.
  4. Utilice remove()el método para eliminar el elemento devuelto más recientemente por el iterador.

Para ver cómo funciona, utilice nuevamente la herramienta Mascota del capítulo Información de tipo:

Iteración simple.java

import java.util.Iterator;
import java.util.List;

public class SimpleIteration {
    
    
    public static void main(String[] args) {
    
    
        List<Pet> pets = new PetCreator().list(12);
        Iterator<Pet> it = pets.iterator();
        while (it.hasNext()) {
    
    
            Pet p = it.next();
            System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
        // A simpler approach, when possible:
        for (Pet p : pets) {
    
    
            System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
        // An Iterator can also remove elements:
        it = pets.iterator();
        for (int i = 0; i < 6; i++) {
    
    
            it.next();
            it.remove();
        }
        System.out.println(pets);
    }
}

Otras categorías relacionadas:

gato.java

public class Cat extends Pet {
    
    
  public Cat(String name) {
    
     super(name); }
  public Cat() {
    
     super(); }
}

Creador.java

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class Creator implements Supplier<Pet> {
    
    
    private Random rand = new Random(47);

    // The different types of Pet to create:
    public abstract List<Class<? extends Pet>> types();

    @Override
    public Pet get() {
    
     // Create one random Pet
        int n = rand.nextInt(types().size());
        try {
    
    
            return types().get(n)
                    .getConstructor().newInstance();
        } catch (InstantiationException |
                 NoSuchMethodException |
                 InvocationTargetException |
                 IllegalAccessException e) {
    
    
            throw new RuntimeException(e);
        }
    }

    public Stream<Pet> stream() {
    
    
        return Stream.generate(this);
    }

    public Pet[] array(int size) {
    
    
        return stream().limit(size).toArray(Pet[]::new);
    }

    public List<Pet> list(int size) {
    
    
        return stream().limit(size).collect(Collectors.toCollection(ArrayList::new));
    }
}

Cymric.java

public class Cymric extends Manx {
    
    
  public Cymric(String name) {
    
     super(name); }
  public Cymric() {
    
     super(); }
}

perro.java

public class Dog extends Pet {
    
    
  public Dog(String name) {
    
     super(name); }
  public Dog() {
    
     super(); }
}

egipcioMau.java

public class EgyptianMau extends Cat {
    
    
  public EgyptianMau(String name) {
    
     super(name); }
  public EgyptianMau() {
    
     super(); }
}

hámster.java

public class Hamster extends Rodent {
    
    
  public Hamster(String name) {
    
     super(name); }
  public Hamster() {
    
     super(); }
}

individuo.java

import java.util.Objects;

public class Individual implements Comparable<Individual> {
    
    
    private static long counter = 0;
    private final long id = counter++;
    private String name;

    public Individual(String name) {
    
    
        this.name = name;
    }

    // 'name' is optional:
    public Individual() {
    
    
    }

    @Override
    public String toString() {
    
    
        return getClass().getSimpleName() +
                (name == null ? "" : " " + name);
    }

    public long id() {
    
    
        return id;
    }

    @Override
    public boolean equals(Object o) {
    
    
        return o instanceof Individual &&
                Objects.equals(id, ((Individual) o).id);
    }

    @Override
    public int hashCode() {
    
    
        return Objects.hash(name, id);
    }

    @Override
    public int compareTo(Individual arg) {
    
    
        // Compare by class name first:
        String first = getClass().getSimpleName();
        String argFirst = arg.getClass().getSimpleName();
        int firstCompare = first.compareTo(argFirst);
        if (firstCompare != 0) {
    
    
            return firstCompare;
        }
        if (name != null && arg.name != null) {
    
    
            int secondCompare = name.compareTo(arg.name);
            if (secondCompare != 0) {
    
    
                return secondCompare;
            }
        }
        return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));
    }
}

manx.java

public class Manx extends Cat {
    
    
  public Manx(String name) {
    
     super(name); }
  public Manx() {
    
     super(); }
}

ratón.java

public class Mouse extends Rodent {
    
    
  public Mouse(String name) {
    
     super(name); }
  public Mouse() {
    
     super(); }
}

Mutt.java

public class Mutt extends Dog {
    
    
  public Mutt(String name) {
    
     super(name); }
  public Mutt() {
    
     super(); }
}

mascota.java

public class Pet extends Individual {
    
    
    public Pet(String name) {
    
    
        super(name);
    }

    public Pet() {
    
    
        super();
    }
}

PetCreator.java

import java.util.*;

public class PetCreator extends Creator {
    
    
    // No try block needed.
    public static final
    List<Class<? extends Pet>> ALL_TYPES = Collections.unmodifiableList(Arrays.asList(
                    Pet.class, Dog.class, Cat.class, Rodent.class,
                    Mutt.class, Pug.class, EgyptianMau.class,
                    Manx.class, Cymric.class, Rat.class,
                    Mouse.class, Hamster.class));
    // Types for random creation:
    private static final
    List<Class<? extends Pet>> TYPES =
            ALL_TYPES.subList(
                    ALL_TYPES.indexOf(Mutt.class),
                    ALL_TYPES.size());

    @Override
    public List<Class<? extends Pet>> types() {
    
    
        return TYPES;
    }

    public static void main(String[] args) {
    
    
        System.out.println(TYPES);
        List<Pet> pets = new PetCreator().list(7);
        System.out.println(pets);
    }
}
/* Output:
[class reflection.pets.Mutt, class reflection.pets.Pug,
class reflection.pets.EgyptianMau, class
reflection.pets.Manx, class reflection.pets.Cymric, class
reflection.pets.Rat, class reflection.pets.Mouse, class
reflection.pets.Hamster]
[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug]
*/

Pug.java

public class Pug extends Dog {
    
    
  public Pug(String name) {
    
     super(name); }
  public Pug() {
    
     super(); }
}

rata.java

public class Rat extends Rodent {
    
    
  public Rat(String name) {
    
     super(name); }
  public Rat() {
    
     super(); }
}

roedor.java

public class Rodent extends Pet {
    
    
  public Rodent(String name) {
    
     super(name); }
  public Rodent() {
    
     super(); }
}

El contenido impreso es el siguiente:

Insertar descripción de la imagen aquí

Con Iterator , ya no tendrás que preocuparte por la cantidad de elementos de la colección. Esto es algo de lo que se ocupa hasNext()y next().

Si solo desea recorrer la Lista hacia adelante y no tiene la intención de modificar el objeto de la Lista en sí, entonces usar la sintaxis for-in es más conciso.

Iterator también puede eliminar next()el último elemento producido por , lo que significa que remove()debe llamarse antes de llamar a next().

La idea de realizar operaciones en cada objeto de una colección es poderosa y se utiliza a lo largo de este libro.

Ahora considere crear un display()método que no necesite saber el tipo exacto de colección:

public class CrossCollectionIteration {
    
    
    public static void display(Iterator<Pet> it) {
    
    
        while (it.hasNext()) {
    
    
            Pet p = it.next();
            System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
    
    
        List<Pet> pets = new PetCreator().list(8);
        LinkedList<Pet> petsLL = new LinkedList<>(pets);
        HashSet<Pet> petsHS = new HashSet<>(pets);
        TreeSet<Pet> petsTS = new TreeSet<>(pets);
        display(pets.iterator());
        display(petsLL.iterator());
        display(petsHS.iterator());
        display(petsTS.iterator());
    }
}

Insertar descripción de la imagen aquí

display()El método no contiene ningún tipo de información sobre la secuencia que atraviesa. Esto también demuestra el verdadero poder de los iteradores : la capacidad de desacoplar la operación de atravesar una secuencia de la estructura subyacente de la secuencia. Por esta razón, a veces decimos que los iteradores unifican el acceso a las colecciones.

Podemos generar una versión más concisa del ejemplo anterior usando la interfaz Iterable , que describe " cualquier cosa que pueda generar un Iterador ":

import java.util.*;

public class CrossCollectionIteration2 {
    
    
    public static void display(Iterable<Pet> ip) {
    
    
        Iterator<Pet> it = ip.iterator();
        while (it.hasNext()) {
    
    
            Pet p = it.next();
            System.out.print(p.id() + ":" + p + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
    
    
        List<Pet> pets = new PetCreator().list(8);
        LinkedList<Pet> petsLL = new LinkedList<>(pets);
        HashSet<Pet> petsHS = new HashSet<>(pets);
        TreeSet<Pet> petsTS = new TreeSet<>(pets);
        display(pets);
        display(petsLL);
        display(petsHS);
        display(petsTS);
    }
}

Insertar descripción de la imagen aquí

Todas las clases aquí son Iterables , por lo que display()la llamada ahora es obviamente más sencilla.

ListaIterador

ListIterator es un subtipo más potente de Iterator que solo puede ser generado por las distintas clases List . Iterator solo puede avanzar, mientras que ListIterator puede moverse en ambas direcciones. Puede generar el índice del elemento anterior y siguiente en la lista donde apunta el iterador y puede set()reemplazar el elemento más reciente que visitó usando el método. Puede generar un ListIteratorlistIterator() que apunte al comienzo de la Lista llamando al método , y también puede crear un ListIterator que apunte al elemento con número de índice n en la lista llamando al método . El siguiente ejemplo demuestra todas estas capacidades:listIterator(n)

import java.util.List;
import java.util.ListIterator;

public class ListIteration {
    
    
  public static void main(String[] args) {
    
    
    List<Pet> pets = new PetCreator().list(8);
    ListIterator<Pet> it = pets.listIterator();
    while(it.hasNext()) {
    
    
      System.out.print(it.next() +
        ", " + it.nextIndex() +
        ", " + it.previousIndex() + "; ");
    }
    System.out.println();
    // Backwards:
    while(it.hasPrevious()) {
    
    
      System.out.print(it.previous().id() + " ");
    }
    System.out.println();
    System.out.println(pets);
    it = pets.listIterator(3);
    while(it.hasNext()) {
    
    
      it.next();
      it.set(new PetCreator().get());
    }
    System.out.println(pets);
  }
}

Insertar descripción de la imagen aquí

new PetCreator().get()Este método se utiliza para reemplazar todos los objetos Pet en la Lista a partir de la posición 3 .

Lista enlazada

LinkedList también implementa la interfaz básica de List como ArrayList , pero es más eficiente que ArrayList cuando realiza operaciones de inserción y eliminación en el medio de List . Sin embargo, es inferior en eficiencia de operación de acceso aleatorio.

LinkedList también agrega métodos que permiten su uso como pila, cola o cola de doble extremo (deque) . Algunos de estos métodos pueden diferir entre sí solo por el nombre, o pueden diferir solo ligeramente para que los nombres sean más apropiados en el contexto de un uso específico (especialmente en Colas ) . Por ejemplo:

  • getFirst()y element()son iguales, ambos devuelven el encabezado de la lista (el primer elemento) sin eliminarlo, y si la Lista está vacía, se lanza una NoSuchElementException . El método es ligeramente diferente de estos dos métodos en que devuelve nulopeek() cuando la lista está vacía .
  • removeFirst()y remove()son iguales, eliminan y devuelven el elemento principal de la lista y arrojan NoSuchElementException cuando la lista está vacía . Con una ligera diferencia, devuelve nulopoll() cuando la lista está vacía .
  • addFirst()Inserta un elemento al principio de la lista.
  • offer()Lo mismo que add()y addLast(). Ambos agregan un elemento al final (final) de la lista.
  • removeLast()Elimina y devuelve el último elemento de la lista.

Los siguientes ejemplos demuestran similitudes y diferencias básicas entre estas características. No repite el comportamiento que se muestra en ListFeatures.java :

public class LinkedListFeatures {
    
    
  public static void main(String[] args) {
    
    
    LinkedList<Pet> pets = new LinkedList<>(new PetCreator().list(5));
    System.out.println(pets);
    // Identical:
    System.out.println("pets.getFirst(): " + pets.getFirst());
    System.out.println("pets.element(): " + pets.element());
    // Only differs in empty-list behavior:
    System.out.println("pets.peek(): " + pets.peek());
    // Identical; remove and return the first element:
    System.out.println("pets.remove(): " + pets.remove());
    System.out.println("pets.removeFirst(): " + pets.removeFirst());
    // Only differs in empty-list behavior:
    System.out.println("pets.poll(): " + pets.poll());
    System.out.println(pets);
    pets.addFirst(new Rat());
    System.out.println("After addFirst(): " + pets);
    pets.offer(new PetCreator().get());
    System.out.println("After offer(): " + pets);
    pets.add(new PetCreator().get());
    System.out.println("After add(): " + pets);
    pets.addLast(new Hamster());
    System.out.println("After addLast(): " + pets);
    System.out.println("pets.removeLast(): " + pets.removeLast());
  }
}

Insertar descripción de la imagen aquí

new PetCreator().list()El resultado se pasa al constructor LinkedList para que pueda usarse para completar LinkedList . Si observa la interfaz Queue , encontrará que agrega los métodos , y basados ​​en LinkedList para que pueda convertirse en una implementación de Queue . Más adelante en este capítulo se proporciona un ejemplo completo de cola .element()offer()peek()poll()remove()

apilarapilar

Una pila es una colección de "último en entrar, primero en salir" (LIFO). A veces se le llama pila pushdown porque el último elemento "empujado" en la pila es el primero "extraído". Una analogía que se utiliza a menudo con una pila es una bandeja de cafetería con un soporte con resorte. La última bandeja cargada es siempre la primera que se saca y se utiliza.

Java 1.0 vino con una clase Stack que resultó estar mal diseñada (para compatibilidad con versiones anteriores, nos vimos obligados a vivir con viejos errores de diseño en Java). Java 6 agregó ArrayDeque , que contiene métodos que implementan la funcionalidad de pila directamente:

import java.util.*;

public class StackTest {
    
    
    public static void main(String[] args) {
    
    
        Deque<String> stack = new ArrayDeque<>();
        for (String s : "My dog has fleas".split(" ")) {
    
    
            stack.push(s);
        }
        while (!stack.isEmpty()) {
    
    
            System.out.print(stack.pop() + " ");
        }
    }
}

Incluso si se usa como pila, todavía tenemos que declararlo como Deque . A veces una clase llamada Stack aclara las cosas:

import java.util.ArrayDeque;
import java.util.Deque;

public class Stack<T> {
    
    
    private Deque<T> storage = new ArrayDeque<>();

    public void push(T v) {
    
    
        storage.push(v);
    }

    public T peek() {
    
    
        return storage.peek();
    }

    public T pop() {
    
    
        return storage.pop();
    }

    public boolean isEmpty() {
    
    
        return storage.isEmpty();
    }

    @Override
    public String toString() {
    
    
        return storage.toString();
    }
}

Aquí se presenta el ejemplo más simple posible de una definición de clase usando genéricos. después del nombre de la claseDígale al compilador que este es un tipo parametrizado y que el parámetro de tipo T será reemplazado por el tipo real cuando use la clase. Básicamente, esta clase indica "Estamos definiendo una pila que puede contener objetos de tipo T " . La pila se implementa utilizando un ArrayDeque , y también se le dice al ArrayDeque que contendrá objetos de tipo T. Tenga en cuenta que acepta un objeto de tipo T y devuelve un objeto de tipo T. El método devolverá el elemento superior de la pila, pero en lugar de eliminarlo de la parte superior de la pila, lo elimina y lo devuelve.push()peek()pop()peek()pop()

Si solo necesita el comportamiento de la pila, entonces usar la herencia no es apropiado, porque esto producirá una clase con todos los demás métodos de ArrayDeque . Usando la composición, puedes elegir qué métodos exponer y cómo nombrarlos.

Esta nueva clase Stack se demostrará a continuación utilizando el mismo código de StackTest.java :

public class StackTest2 {
    
    
    public static void main(String[] args) {
    
    
        Stack<String> stack = new Stack<>();
        for (String s : "My dog has fleas".split(" ")) {
    
    
            stack.push(s);
        }
        while (!stack.isEmpty()) {
    
    
            System.out.print(stack.pop() + " ");
        }
    }
}

Si desea utilizar esta clase Stack en su propio código , debe especificar completamente el nombre del paquete al crear su instancia o cambiar el nombre de la clase; de ​​lo contrario, puede entrar en conflicto con Stack en el paquete java.util . Por ejemplo, si importamos java.util. * en el ejemplo anterior , entonces se debe utilizar el nombre del paquete para evitar conflictos:

public class StackCollision {
    
    
    public static void main(String[] args) {
    
    
        Stack<String> stack = new Stack<>();
        for (String s : "My dog has fleas".split(" ")) {
    
    
            stack.push(s);
        }
        while (!stack.isEmpty()) {
    
    
            System.out.print(stack.pop() + " ");
        }
        System.out.println();
        java.util.Stack<String> stack2 =
                new java.util.Stack<>();
        for (String s : "My dog has fleas".split(" ")) {
    
    
            stack2.push(s);
        }
        while (!stack2.empty()) {
    
    
            System.out.print(stack2.pop() + " ");
        }
    }
}

Insertar descripción de la imagen aquí

Aunque ya existe java.util.Stack , ArrayDeque produce una pila mejor y, por lo tanto, es preferible.

También puede utilizar importaciones explícitas para controlar la selección de la implementación de Stack "preferida" :

import onjava.Stack;

Cualquier referencia a Stack ahora seleccionará la versión onjava y, al seleccionar java.util.Stack , se debe usar el nombre completo.

Supongo que te gusta

Origin blog.csdn.net/GXL_1012/article/details/132588756
Recomendado
Clasificación