Java编程思想学习笔记(11)

Java编程思想学习笔记(11)

持有对象

通常,程序总是根据运行时才知道的某些条件去创建新对象,在此之前,不会知道所需对象的数量,甚至不知道确切的类型。为解决这个普遍的问题,需要在任意时刻和任意位置创建任意数量的对象,所以就不能依靠创建命名的引用来持有每一个对象:

如 MyType aReference;

因为你实际上不知道需要多少个这样的引用。

大多数语言都提供某种方法来解决这个基本问题。Java有多种方式保存对象,例如数组,但是数组具有固定的尺寸,而在更一般的情况下,你在写程序时并不知道需要多少个对象,或者是否需要更加复杂的方式来存储对象,因此数组尺寸固定这一限制就显得过于受限了。

Java提供了一套相当完整的容器类来解决这个问题。其中基本的类型是List,Set,Queue,Map。

这些对象被称为集合类。

泛型和类型安全的容器

用Java SE5之前的容器的一个主要问题就是编译器允许你向容器插入不正确的类型。

class Apple {
        private static long counter;
        private final long id = counter++;
        public long id() { return id; }
}


class Orange {} 


public class ApplesAndOrangesWithoutGenerics {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
        ArrayList apples = new ArrayList();
        for(int i = 0; i < 3; i++)
            apples.add(new Apple());
        // Not prevented from adding an Orange to apples:
        apples.add(new Orange());
        for(int i = 0; i < apples.size(); i++)
            ((Apple)apples.get(i)).id();
        // Orange is detected only at run time
        }
}


上边,Apple和Orange都放在了容器中,然后将它们取出来。正常情况下,Java编译器会报告警告信息,因为没有使用泛型,这里使用@SuppressWarnings注解及其参数表示只有有关“不受检查的异常”的警告信息应该被抑制。

因为ArrayList保存的是Objects对象,所以可以通过add方法将Apple对象放进容器中,还可以添加Orange对象,而且无论是在编译期还是运行时都没有问题。

当你用get方法取出来你觉得是Apple的对象时,你得到的只是Object的引用,必须将其转型为Apple。否则你就会得到语法错误。

要想定义用来保存Apple对象的ArrayList,可以声明ArrayList,其中尖括号包起来的是类型参数(可以有多个),它指定了这个容器实例可以保存的类型。

public class ApplesAndOrangesWithGenerics {
    public static void main(String[] args) {
        ArrayList<Apple> apples = new ArrayList<Apple>();
        for(int i = 0; i < 3; i++)
            apples.add(new Apple());
        // Compile-time error:
        // apples.add(new Orange());
        for(int i = 0; i < apples.size(); i++)
                System.out.println(apples.get(i).id());
        // Using foreach:
        for(Apple c : apples)
            System.out.println(c.id());
    }
}

当你指定了某个类型作为泛型参数时,你不仅可以将该确切类型的对象放置到容器中,向上转型也可以像作用于其它类型那样作用于泛型。

public abstract class People {

    abstract void where();
    
}


public class Chinese extends People{

    private String string;

    public Chinese() {
        this.string = "Chinese";
    }

    @Override
    void where() {
        System.out.println(string);
    }
}

public class Janpanse extends People{

    private String string;

    public Janpanse() {
        this.string = "Janpanse";
    }

    @Override
    void where() {
        System.out.println(string);
    }
}

public class GenericsAndUpcasting {

    public static void main(String[] args) {

        ArrayList<People> peoples = new ArrayList<>();

        peoples.add(new Chinese());

        peoples.add(new Janpanse());

        for (People people : peoples) {
            people.where();
        }


    }
}

基本概念

Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:

  • 1 Collection 一个独立元素的序列,这些元素都服从一条或多条规则

  • 1.1 List必须按照插入顺序保存元素

  • 1.2 Set不能有重复元素

  • 1.3 Queue按照队列规则来确定对象产生的顺序

  • 2 Map。一组键值对对象。

你应该创建一个具体类的对象,然后将其转型为对应的接口,然后在其余的代码中都使用这个接口。

Collection接口概括了序列的概念—————— 一种存放一组对象的方式。

public class SimpleCollection {
    public static void main(String[] args) {
        Collection<Integer> c = new ArrayList<Integer>();
        for(int i = 0; i < 10; i++)
            c.add(i); // Autoboxing
        for(Integer i : c)
            System.out.print(i + ", ");
    }
}

上面例子只使用了Collection,任何继承自Collection类的对象都可以正常工作。

添加一组元素

Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个List对象。Collections.addAll( ) 方法接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,将元素添加到Collection中。

public class AddingGroups {

    public static void main(String[] args) {

        Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));

        Integer[] moreInts = { 6, 7, 8, 9, 10 };

        collection.addAll(Arrays.asList(moreInts));

// Runs significantly faster, but you can’t
// construct a Collection this way:

        Collections.addAll(collection, 11, 12, 13, 14, 15);

        Collections.addAll(collection, moreInts);

// Produces a list "backed by" an array:
        List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);
        list.set(1, 99); // OK -- modify an element

//        Exception in thread "main" java.lang.UnsupportedOperationException
// list.add(21); // Runtime error because the
// underlying array cannot be resized.

    }
}

Collection的构造器可以接受另一个Collection,用来将自身初始化。因此你可以使用
Arrays.asList()来为这个构造器产生输入。

但是Collections.addAll() 方法运行的要快很多,而且构建一个不包含元素的Collection,然后调用Collections.addAll()这种方式是很方便的。

Collection.addAll()方法只能接受另一个Collection对象作为参数,因此它不如Arrays.asList() 或者Collections.addAll()灵活。这两个方法使用的都是可变参数列表。

也可以直接使用Arrays.asList()的输出,将其当做List,但是在这种情况下,其底层表示是数组,因此不能调整尺寸。

class Car {
}

public class CarUseGas extends Car{
}


public class CarUseElec extends Car{
}

public class Audi extends CarUseGas{
}

public class TesLa extends CarUseElec{
}

public class BMW extends CarUseGas{
}



public class AsListInference {


    public static void main(String[] args) {

        List<Car> c1 = Arrays.asList(
                new CarUseElec(),new CarUseGas()
        );

//        不会编译

        List<Car> c2 = Arrays.asList(
                new Audi(),new BMW()
        );

        List<Car> c3 = new ArrayList<Car>();

        Collections.addAll(c3,new Audi(),new TesLa());

        List<Car> c4 = Arrays.<Car>asList(
                new Audi(),new TesLa()
        );

//        System.out.println(c2.get(0).getClass());


    }

}

当创建c2时,Arrays.asList()只有 CarUseGas类型,因此它会创建List< CarUseGas >而不是List< Car >.

容器的打印

public class PrintingContainers {

    static Collection fill(Collection<String> collection) {
        collection.add("rat");
        collection.add("cat");
        collection.add("dog");
        collection.add("dog");
        return collection;
    }
    static Map fill(Map<String,String> map) {
        map.put("rat", "Fuzzy");
        map.put("cat", "Rags");
        map.put("dog", "Bosco");
        map.put("dog", "Spot");
        return map;
    }
    public static void main(String[] args) {

        print(fill(new ArrayList<String>()));

        print(fill(new LinkedList<String>()));

        print(fill(new HashSet<String>()));

        print(fill(new TreeSet<String>()));

        print(fill(new LinkedHashSet<String>()));

        print(fill(new HashMap<String,String>()));

        print(fill(new TreeMap<String,String>()));

        print(fill(new LinkedHashMap<String,String>()));
    }


}

上面展示了Java容器类库中的两种主要类型,区别在于容器中每个“槽”保存的元素个数。

Collection每个槽中只能保存一个元素,Map在每个槽内保存两个元素。

List

先贴上将要使用的Pet类及其子类以及一些方法:

public class Individual {

    private static long counter = 0;

    private final long id = counter++;

    private String name;

    public long id(){
        return id;
    }

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

    public Individual() {
    }

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


public class Person extends Individual{

    public Person(String name) {
        super(name);
    }

    public Person() {
    }
}

public class Pet extends Individual{

    public Pet(String name) {
        super(name);
    }

    public Pet() {
    }
}

public class Cat extends Pet{

    public Cat(String name) {
        super(name);
    }

    public Cat() {
    }
}

public class Dog extends Pet{

    public Dog(String name) {
        super(name);
    }

    public Dog() {
    }
}

public class Rodent extends Pet{


//    啮齿动物
    public Rodent(String name) {
        super(name);
    }

    public Rodent() {
    }
}

public class Manx extends Cat{


//    马恩岛猫
    public Manx(String name) {
        super(name);
    }

    public Manx() {
    }
}

public class Cymric extends Manx{

//    威尔斯猫

    public Cymric(String name) {
        super(name);
    }

    public Cymric() {
    }
}

public class EgyptianMau extends Cat{

//    埃及猫
    public EgyptianMau(String name) {
        super(name);
    }

    public EgyptianMau() {
    }
}



public class Rat extends Rodent{

    public Rat(String name) {
        super(name);
    }

    public Rat() {
    }
}

public class Hamster extends Rodent{

//    仓鼠
    public Hamster(String name) {
        super(name);
    }

    public Hamster() {
    }
}



public class Pug extends Dog{


//     哈巴狗
    public Pug(String name) {
        super(name);
    }

    public Pug() {
    }
}

public class Mouse extends Rodent{

    public Mouse(String name) {
        super(name);
    }

    public Mouse() {
    }
}

public class Mutt extends Dog{


//    杂种狗
    public Mutt(String name) {
        super(name);
    }

    public Mutt() {
    }
}

public abstract class PetCreator {

    private Random random = new Random(47);

    public abstract List<Class<? extends Pet>> types();

    public Pet randomPet(){

        int n = random.nextInt(types().size());

        try {
            return types().get(n).newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e){
            throw new RuntimeException(e);
        }
    }

    public Pet[] createArray(int size){

        Pet[] result = new Pet[size];

        for (int i = 0; i < size ; i++) {
            result[i] = randomPet();
        }

        return result;
    }


    public ArrayList<Pet> arrayList(int size){

        ArrayList<Pet> result = new ArrayList<>();

        Collections.addAll(result,createArray(size));

        return result;
    }
}


public class LiteralPetCreator extends PetCreator{

    // No try block needed.
    @SuppressWarnings("unchecked")
    public static final List<Class<? extends Pet>> allTypes =
            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 =
            allTypes.subList(allTypes.indexOf(Mutt.class),allTypes.size());
    public List<Class<? extends Pet>> types() {
        return types;
    }
}

public class Pets {

    public static final PetCreator creator =
            new LiteralPetCreator();
    public static Pet randomPet() {
        return creator.randomPet();
    }
    public static Pet[] createArray(int size) {
        return creator.createArray(size);
    }
    public static ArrayList<Pet> arrayList(int size) {
        return creator.arrayList(size);
    }


}


有两种类型的List:

  • ArrayList

  • LinkedList

例子:

public class ListFeatures {

    public static void main(String[] args) {


        Random rand = new Random(47);
        List<Pet> pets = Pets.arrayList(7);
        print("1: " + pets);
        Hamster h = new Hamster();
        pets.add(h); // Automatically resizes
        print("2: " + pets);
        print("3: " + pets.contains(h));
        pets.remove(h); // Remove by object
        Pet p = pets.get(2);
        print("4: " + p + " " + pets.indexOf(p));
        Pet cymric = new Cymric();
        print("5: " + pets.indexOf(cymric));
        print("6: " + pets.remove(cymric));
// Must be the exact object:
        print("7: " + pets.remove(p));
        print("8: " + pets);
        pets.add(3, new Mouse()); // Insert at an index
        print("9: " + pets);
        List<Pet> sub = pets.subList(1, 4);
        print("subList: " + sub);
        print("10: " + pets.containsAll(sub));
//        Collections.sort(sub); // In-place sort
        print("sorted subList: " + sub);
// Order is not important in containsAll():
        print("11: " + pets.containsAll(sub));
        Collections.shuffle(sub, rand); // Mix it up
        print("shuffled subList: " + sub);
        print("12: " + pets.containsAll(sub));
        List<Pet> copy = new ArrayList<Pet>(pets);
        sub = Arrays.asList(pets.get(1), pets.get(4));
        print("sub: " + sub);
        copy.retainAll(sub);
        print("13: " + copy);
        copy = new ArrayList<Pet>(pets); // Get a fresh copy
        copy.remove(2); // Remove by index
        print("14: " + copy);
        copy.removeAll(sub); // Only removes exact objects
        print("15: " + copy);
        copy.set(1, new Mouse()); // Replace an element
        print("16: " + copy);
        copy.addAll(2, sub); // Insert a list in the middle
        print("17: " + copy);
        print("18: " + pets.isEmpty());
        pets.clear(); // Remove all elements
        print("19: " + pets);
        print("20: " + pets.isEmpty());
        pets.addAll(Pets.arrayList(4));
        print("21: " + pets);
        Object[] o = pets.toArray();
        print("22: " + o[3]);
        Pet[] pa = pets.toArray(new Pet[0]);
//        print("23: " + pa[3].id());

    }

//    第一行输出展示了最初的由Pet构成的List,与数组不同,List允许在它被创建之后添加元素,
//    移除元素,或者自我调整尺寸,
//    第二行,可以看到添加了一个Hamster
//    用contains方法来确定某个对象是否在列表中
//    subList允许从较大的列表中创建出一个片断
//    retainAll方法有效的交集操作
}



迭代器

使用容器,必须对容器的确切类型进行编程,这样并不会,比如你最开始是对着List编码的,但是后来发现如果能够把相同的代码应用于Set,将会显得更加方便,此时应该怎么做呢?或者打算从头开始编写通用的代码,它们只是使用容器,不知道或者说不关心容器的类型。

迭代器可以达到这一目的,迭代器是一个对象,它的工作是遍历并选择序列中的对象,而不必关系该序列底层的结构。

Java的Iterator只能单向移动,只能用来:

  • 使用方法 iterator()要求容器返回一个Iterator,Iterator将会准备好返回序列的第一个元素

  • 使用next()获得序列的下一个元素

  • 使用hasNext()检查序列中是否还有元素

  • 使用remove()将迭代器新近返回的元素删除

例子:

public class SimpleIteration {

    public static void main(String[] args) {
        List<Pet> pets = Pets.arrayList(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);
    }
}

考虑创建一个display方法,它不必知晓容器的确切类型:

public class CrossContainerIteration {

    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) {
        ArrayList<Pet> pets = Pets.arrayList(8);
        LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
        HashSet<Pet> petsHS = new HashSet<Pet>(pets);
//        TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);
        display(pets.iterator());
        display(petsLL.iterator());
        display(petsHS.iterator());
//        display(petsTS.iterator());
    }


}


display方法不包含任何有关它所遍历的序列的类型信息,而这也显示了Iterator的真正威力,能够将遍历序列的操作与序列底层的结构分离。

ListIterator

ListIterator是一个更加强大的Iterator的子类型。她只能用于各种List类的访问,尽管Iterator只能向前移动,但是ListIterator可以双向移动,它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引。

public class ListIteration {

    public static void main(String[] args) {
        List<Pet> pets = Pets.arrayList(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(Pets.randomPet());
        }
        System.out.println(pets);
    }
}

LinkedList

LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作(在List中间插入和移除)时比ArrayList更加高效。

LinkedList还添加了可以使其用作栈,队列,双端队列的方法。

例子:

public class LinkedListFeatures {

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


}


Stack

LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList当做栈使用,不过了解一下栈的实现可以更好的了解它的内部机制。

public class Stack<T> {

    private LinkedList<T> storage = new LinkedList<T>();
    public void push(T v) { storage.addFirst(v); }
    public T peek() { return storage.getFirst(); }
    public T pop() { return storage.removeFirst(); }
    public boolean empty() { return storage.isEmpty(); }
    public String toString() { return storage.toString(); }
}

进行演示:

public class StackTest {

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


public class StackCollision {

    public static void main(String[] args) {
        Stack<String> stack =
                new Stack<String>();

        for(String s : "My dog has fleas".split(" "))
            stack.push(s);

        while(!stack.empty())
            System.out.print(stack.pop() + " ");


        System.out.println();

        java.util.Stack<String> stack2 =
                new java.util.Stack<String>();


        for(String s : "My dog has fleas".split(" "))
            stack2.push(s);


        while(!stack2.empty())
            System.out.print(stack2.pop() + " ");
    }


}

Set

Set不保存重复元素。

Set具有和Collection完全一样的接口,因此没有额外的功能。实际上,Set就是Collection,只是行为不同。

例子:


public class SetOfInteger {

    public static void main(String[] args) {
        Random rand = new Random(47);
        Set<Integer> intset = new HashSet<Integer>();
        for(int i = 0; i < 10000; i++)
            intset.add(rand.nextInt(30));
        System.out.println(intset);
    }


}

0到29之间的10000个随机数被添加到了Set中,其中没有重复的数字出现。

并且结果也是乱序的,如果想对结果排序,一种方式是使用TreeSet代替HashSet。

public class SortedSetOfInteger {

    public static void main(String[] args) {
        Random rand = new Random(47);
        SortedSet<Integer> intset = new TreeSet<Integer>();
        for(int i = 0; i < 10000; i++)
            intset.add(rand.nextInt(30));
        System.out.println(intset);
    }


}

接下来使用contains()测试Set的归属性。

public class SetOperations {

    public static void main(String[] argsStr) {
            Set<String> set1 = new HashSet<String>();
        Collections.addAll(set1,
                "A B C D E F G H I J K L".split(" "));
        set1.add("M");
        print("H: " + set1.contains("H"));
        print("N: " + set1.contains("N"));
        Set<String> set2 = new HashSet<String>();
        Collections.addAll(set2, "H I J K L".split(" "));
        print("set2 in set1: " + set1.containsAll(set2));
        set1.remove("H");
        print("set1: " + set1);
        print("set2 in set1: " + set1.containsAll(set2));
        set1.removeAll(set2);
        print("set2 removed from set1: " + set1);
        Collections.addAll(set1, "X Y Z".split(" "));
        print("‘X Y Z’ added to set1: " + set1);
    }


}

Map

下面,键是由Random随机产生的数字,而值是该数字出现的次数。

public class Statistics {

    public static void main(String[] args) {

        Random rand = new Random(47);

        Map<Integer,Integer> m =
                new HashMap<Integer,Integer>();

        for(int i = 0; i < 10000; i++) {
// Produce a number between 0 and 20:
            int r = rand.nextInt(20);
            Integer freq = m.get(r);
            m.put(r, freq == null ? 1 : freq + 1);
        }


        System.out.println(m);
    }


}


下面这个使用一个String描述来查找Pet,并且使用containsKey()和containsValue()来测试一个Map,查看它是否包含某个键或者值。

public class PetMap {

    public static void main(String[] args) {
        Map<String, Pet> petMap = new HashMap<String,Pet>();
        petMap.put("My Cat", new Cat("Molly"));
        petMap.put("My Dog", new Dog("Ginger"));
        petMap.put("My Hamster", new Hamster("Bosco"));
        print(petMap);
        Pet dog = petMap.get("My Dog");
        print(dog);
        print(petMap.containsKey("My Dog"));
        print(petMap.containsValue(dog));
    }


}


扩展到多维:

public class MapOfList {

    public static Map<Person, List<? extends Pet>>
            petPeople = new HashMap<Person, List<? extends Pet>>();
    
    
    static {
        petPeople.put(new Person("Dawn"),
                Arrays.asList(new Cymric("Molly"),new Mutt("Spot")));
        
        
        petPeople.put(new Person("Kate"),
                Arrays.asList(new Cat("Shackleton"),
                        new Cat("Elsie May"), new Dog("Margrett")));
        
        
        petPeople.put(new Person("Marilyn"),
                Arrays.asList(
                        new Pug("Louie aka Louis Snorkelstein Dupree"),
                        new Cat("Stanford aka Stinky el Negro"),
                        new Cat("Pinkola")));
        
        
        petPeople.put(new Person("Luke"),
                Arrays.asList(new Rat("Fuzzy"), new Rat("Fizzy")));
        
        
        petPeople.put(new Person("Isaac"),
                Arrays.asList(new Rat("Freckly")));
        
    }
    public static void main(String[] args) {
        print("People: " + petPeople.keySet());
        
        print("Pets: " + petPeople.values());
        
        for(Person person : petPeople.keySet()) {
            print(person + " has:");
            
            for(Pet pet : petPeople.get(person))
                print(" " + pet);
        }
    }
}

Queue

LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口。


public class QueueDemo {

    public static void printQ(Queue queue) {
        while(queue.peek() != null)
            System.out.print(queue.remove() + " ");
        System.out.println();
    }
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<Integer>();
        Random rand = new Random(47);
        for(int i = 0; i < 10; i++)
            queue.offer(rand.nextInt(i + 10));
        printQ(queue);
        Queue<Character> qc = new LinkedList<Character>();
        for(char c : "Brontosaurus".toCharArray())
            qc.offer(c);
        printQ(qc);
    }
}

PriorityQueue

优先级队列声明下一个弹出元素是最需要的元素

当你在PriorityQueue上调用offer方法来插入一个对象时,这个对象会在队列中被排序,默认的排序将使用对象在队列中的自然顺序,但是你可以通过提供自己的Comparator来修改这个顺序。

public class PriorityQueueDemo {

    public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue =
                new PriorityQueue<Integer>();
        Random rand = new Random(47);
        for(int i = 0; i < 10; i++)
            priorityQueue.offer(rand.nextInt(i + 10));
        QueueDemo.printQ(priorityQueue);
        List<Integer> ints = Arrays.asList(25, 22, 20,
                18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
        priorityQueue = new PriorityQueue<Integer>(ints);
        QueueDemo.printQ(priorityQueue);
        priorityQueue = new PriorityQueue<Integer>(
                ints.size(), Collections.reverseOrder());
        priorityQueue.addAll(ints);
        QueueDemo.printQ(priorityQueue);
        String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
        List<String> strings = Arrays.asList(fact.split(""));
        PriorityQueue<String> stringPQ =
                new PriorityQueue<String>(strings);
        QueueDemo.printQ(stringPQ);
        stringPQ = new PriorityQueue<String>(
                strings.size(), Collections.reverseOrder());
        stringPQ.addAll(strings);
        QueueDemo.printQ(stringPQ);
        Set<Character> charSet = new HashSet<Character>();
        for(char c : fact.toCharArray())
            charSet.add(c); // Autoboxing
        PriorityQueue<Character> characterPQ =
                new PriorityQueue<Character>(charSet);
        QueueDemo.printQ(characterPQ);
    }


}

Collection和Iterator

Collection是描述所有序列容器的共性的接口,它可能被认为是一个“附属接口”,因为要表示其他若干个接口的共性而出现的接口。另外,java.utiL.AbstractCollection提供了Collection的默认实现,使得你可以创建AbstractCollection的子类型,而其中没有不必要的代码重复。

用迭代器而不是Collection来表示容器之间的共性,但是,这两种方法绑定到了一起,因为实现Collection就意味着需要提供iterator()方法。

public class InterfaceVsIterator {

    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 display(Collection<Pet> pets) {
        for (Pet p : pets)
            System.out.print(p.id() + ":" + p + " ");
        System.out.println();
    }

    public static void main(String[] args) {
        List<Pet> petList = Pets.arrayList(8);
        Set<Pet> petSet = new HashSet<Pet>(petList);
        Map<String, Pet> petMap =
                new LinkedHashMap<String, Pet>();

        String[] names = ("Ralph, Eric, Robin, Lacey, " +
                "Britney, Sam, Spot, Fluffy").split(", ");
        for (int i = 0; i < names.length; i++)
            petMap.put(names[i], petList.get(i));
        display(petList);
        display(petSet);
        display(petList.iterator());
        display(petSet.iterator());
        System.out.println(petMap);
        System.out.println(petMap.keySet());
        display(petMap.values());
        display(petMap.values().iterator());
    }
}

两个版本的display方法都可以使用Map或Collection的子类型来工作,而且Collection接口和Iterator都可以将display方法以底层容器的特定实现解耦。

public class CollectionSequence extends AbstractCollection<Pet> {

    private Pet[] pets = Pets.createArray(8);

    public int size() { return pets.length; }
    public Iterator<Pet> iterator() {
        return new Iterator<Pet>() {
            private int index = 0;
            public boolean hasNext() {
                return index < pets.length;
            }
            public Pet next() { return pets[index++]; }
            public void remove() { // Not implemented
                throw new UnsupportedOperationException();
            }
        };
    }


    public static void main(String[] args) {
        CollectionSequence c = new CollectionSequence();
        InterfaceVsIterator.display(c);
        InterfaceVsIterator.display(c.iterator());
    }
}



上面可以看出,如果你实现Collection,就必须实现iterator(),只拿实现iterator()与继承AbstractCollection相比,花费的代价只是略小。

如果你的类已经继承其它类,那么你要实现Collection就必须实现该接口中的所有方法。

此时,继承并提供创建迭代器的能力就会容易的多。


class PetSequence {

    protected Pet[] pets = Pets.createArray(8);

}


public class NonCollectionSequence extends PetSequence{

    public Iterator<Pet> iterator() {
        return new Iterator<Pet>() {
            private int index = 0;
            public boolean hasNext() {
                return index < pets.length;
            }
            public Pet next() { return pets[index++]; }
            public void remove() { // Not implemented
                throw new UnsupportedOperationException();
            }
        };
    }
    public static void main(String[] args) {
        NonCollectionSequence nc = new NonCollectionSequence();
        InterfaceVsIterator.display(nc.iterator());
    }
}


Foreach与迭代器

foreach语法主要用于数组,但是也可以用于任何Collection对象


public class ForEachCollections {

    public static void main(String[] args) {
        Collection<String> cs = new LinkedList<String>();
        Collections.addAll(cs,
                "Take the long way home".split(" "));
        for(String s : cs)
            System.out.print("‘" + s + "‘ ");
    }


}

上边之所以能够工作是因为Java SE5引入了新的Iterable接口,该接口包含一个能够产生
Iterator的iterator方法,并且Iterable接口被foreach用来在序列中移动,因此如果你创建了任何实现Iterable的类,都可以将它用在foreach语句中。

public class IterableClass implements Iterable<String>{

    protected String[] words = ("And that is how " +
            "we know the Earth to be banana-shaped.").split(" ");


    public Iterator<String> iterator() {
        return new Iterator<String>() {
            private int index = 0;
            public boolean hasNext() {
                return index < words.length;
            }
            public String next() { return words[index++]; }
            public void remove() { // Not implemented
                throw new UnsupportedOperationException();
            }
        };
    }


    public static void main(String[] args) {
        for(String s : new IterableClass())
            System.out.print(s + " ");
    }
}

iterator方法返回的是实现了Iterator的匿名内部类的实例,该匿名内部类可以遍历数组中的所有单词。

在Java SE5中,大量的类都是Iterable类型,主要包括所有的Collection类(不包括Map类)。

下面可以显示所有操作系统的环境变量:

public class EnvironmentVariables {

    public static void main(String[] args) {
        for(Map.Entry entry: System.getenv().entrySet()) {
            System.out.println(entry.getKey() + ": " +
                    entry.getValue());
        }
    }

}

foreach语句可以用于数组或者其他任何Iterable,但是这并不意味着数组肯定也是一个Iterable,而且任何自动包装也不会自动发生

public class ArrayIsNotIterable {

    static <T> void test(Iterable<T> ib) {
        for(T t : ib)
            System.out.print(t + " ");
    }
    public static void main(String[] args) {
        test(Arrays.asList(1, 2, 3));
        String[] strings = { "A", "B", "C" };
// An array works in foreach, but it’s not Iterable:
//! test(strings);
// You must explicitly convert it to an Iterable:
        test(Arrays.asList(strings));
    }


}

发布了189 篇原创文章 · 获赞 58 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/Coder_py/article/details/103818652