Java编程思想--11持有对象--容器

11.1 泛型和类型安全容器

当使用泛型容器时,情况比较复杂,相比较之下预定义类型比较简单。
当指定某个类型作为泛型参数时,也可以将向上转型的对象加入其中。

11.2 基本概念

Java容器类库用途是保存对象,可以划分为两个不同的概念:

  1. Collection。一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入顺序保存元素,而Set不能有重复元素。Queue按照排队规则来确定对象产生的顺序(通常与插入顺序相同)。
  2. Map。一组成对的“键值对”对象,允许使用键来查找值。ArrayList允许使用数字来查找值。映射表允许使用一个对象来查找另一个对象,称为“关联数组”或“字典”。

所有Collection都可以用foreach语法遍历。

11.3 添加一组元素

  1. 可以调用Collection对象的addAll+Arrays.asList方法
  2. 或者使用Collections.addAll方法,该方法第一个参数时Collection对象,后面支持普通对象数组、可变参数列表,或者Arrays.asList。

方法一较快,但是方法二的适应新更好一些(键第二段代码,线下实验不会报错了,目前版本jdk-14.0.1)。

需要注意的是Arrays.asList返回的是List,不能调整尺寸。

import java.util.*;
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));
    // collection.addAll(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
    // list.add(21); // Runtime error because the
                     // underlying array cannot be resized.
  }
} ///:~
import java.util.*;

class Snow {}
class Powder extends Snow {}
class Light extends Powder {}
class Heavy extends Powder {}
class Crusty extends Snow {}
class Slush extends Snow {}

public class AsListInference {
  public static void main(String[] args) {
    List<Snow> snow1 = Arrays.asList(
      new Crusty(), new Slush(), new Powder());

    // Won't compile:
    List<Snow> snow2 = Arrays.asList(
      new Light(), new Heavy());
    // Compiler says:
    // found   : java.util.List<Powder>
    // required: java.util.List<Snow>

    // Collections.addAll() doesn't get confused:
    List<Snow> snow3 = new ArrayList<Snow>();
    Collections.addAll(snow3, new Light(), new Heavy());

    // Give a hint using an
    // explicit type argument specification:
    List<Snow> snow4 = Arrays.<Snow>asList(
       new Light(), new Heavy());
  }
} ///:~

11.4 容器的打印

Map与Collection都有toString方法。

11.5 List

List接口在Collection的基础上添加了大量方法,使得可以在List中插入和移除元素。

  • ArrayList,擅长随机访问元素,但是插入和已出元素时较慢。
  • LinkedList,插入删除较快,但是随机访问方面较慢。

11.6 迭代器

11.6.1 基本操作

迭代式是一个对象,可以遍历选择序列中的对象。此外迭代器是轻量级对象,创建它的开销很小。Java中迭代器效果如下:

  1. 调用容器的List对象的iterator()便可以获得相应的迭代器。
  2. 迭代器只能单向移动。
  3. 使用next()方法获取序列中的下一个元素。
  4. 使用hasNext()来检查序列中是否还含有元素。
  5. 使用remove()方法将迭代器当前的元素删除(jdk-14.0.1版本,该方法运行时会报错)。
public class SimpleIteration {
  public static void main(String[] args) {
    List<Integer> pets = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
    Iterator<Integer> it = pets.iterator();
    while(it.hasNext()) {
        Integer p = it.next();
      System.out.print("i = "+ p + " ");
    }
    System.out.println();
    // A simpler approach, when possible:
    for(Integer p : pets)
      System.out.print("i = "+ p + " ");
    System.out.println();	
    // An Iterator can also remove elements:
    it = pets.iterator();
    while(it.hasNext()){
        it.next();
        it.remove();
    }
    System.out.println(pets);
  }
} /* Output:
i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 0
i = 1 i = 2 i = 3 i = 4 i = 5 i = 6 i = 7 i = 8 i = 9 i = 0
Exception in thread "main" java.lang.UnsupportedOperationException: remove
        at java.base/java.util.Iterator.remove(Iterator.java:102)
        at chapter11.SimpleIteration.main(SimpleIteration.java:22)
*///:~

11.6.2 优点

  1. 无需担心元素的数量,可以由next()与hasNext()执行。
  2. 无需知晓特点的类型(将迭代器向上转型成Iterator)。
import java.util.*;

public class CrossContainerIteration {
  public static void display(Iterator<Integer> it) {
    while(it.hasNext()) {
        Integer p = it.next();
        System.out.print(p + " ");
    }
    System.out.println();
  }	
  public static void main(String[] args) {
    ArrayList<Integer> pets = new ArrayList<>();
    for (int i=0; i < 10;  i++)
        pets.add(i);
    LinkedList<Integer> petsLL = new LinkedList<Integer>(pets);
    HashSet<Integer> petsHS = new HashSet<Integer>(pets);
    TreeSet<Integer> petsTS = new TreeSet<Integer>(pets);
    display(pets.iterator());
    display(petsLL.iterator());
    display(petsHS.iterator());
    display(petsTS.iterator());
  }
} /* Output:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
*///:~

11.6.3 ListIterator

可以向前与向后移动的迭代器。

import java.util.*;

public class ListIteration {
  public static void main(String[] args) {
    List<Integer> pets = new ArrayList<>();
    for(int i=0; i<10; i++)
        pets.add(i+1);
    // List<Integer> pets = Pets.arrayList(8);
    ListIterator<Integer> it = pets.listIterator();
    while(it.hasNext())
      System.out.print(it.next() + ", " + it.nextIndex() +
        ", " + it.previousIndex() + "; ");
    System.out.println();
    // Backwards:
    while(it.hasPrevious()){
        it.previous();
        System.out.print(it.previousIndex() + " ");
    }
        
    //   System.out.print(it.previous().id() + " ");
    System.out.println();
    System.out.println(pets);	
    it = pets.listIterator(3);
    while(it.hasNext()) {
      it.next();
      it.set(0);
      it.remove();
    }
    System.out.println(pets);
  }
}

11.7 LinkedList

11.7.1 获取元素

获取表头元素

  • getFirst
  • element
  • peek

11.7.2 删除表头元素

  • remove()
  • removeFirst()
  • poll()

11.7.3 添加元素

  • addFirst():向表头添加元素
  • add():队尾添加
  • offer():队尾添加
  • addLast:队尾添加

11.8 Stack

先进后出。

11.9 Set

保证集合内的元素不重复。

  • contains():
  • containsAll():
  • remove()
  • removeAll()

11.10 Map

Map提供键值对的映射。

import java.util.*;

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);
  }
}

可以拓展到多维

Map<Integer, List<Integer>>

11.11 Queue

队列是一个先进先出的容器,即从容器的一端放入事物,然后从一端取出。


LinkedList提供了方法支持Queue的行为,因此可以将LinkedList向上转型为Queue。

  • offer():插入元素到队尾。
  • peek():在不移除的情况下返回队头,队列为空时返回null。
  • element():在不移除的情况下返回队头,队列为空时抛出异常。
  • poll():删除并返回队头,队列为空时返回null。
  • remove():删除并返回队头,队列为空时抛出异常。

11.11.1 PriorityQueue

优先级队列。该队列会根据优先级顺序自动对元素进行排序。

  • 优先级队列允许重复。
  • 最小的值拥有最高的优先级。
  • 可以提供自己的Comparator来改变排序。
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);
// 添加Collections.reverseOrder()来使得优先级队列反序。
priorityQueue = new PriorityQueue<Integer>(
        ints.size(), Collections.reverseOrder());

11.12 Collections和Iterator

Collection是描述所有序列容器的共性的根接口。希望接口描述的一个理由是它可以使我们创建更通用的代码。通过接口而非具体的实现来编写代码。
如果编写的方法将接受一个Collection,那么该方法就可以应用于任何实现了Collection的类–这也使得一个新类可以选择去实现Collection接口,以便我们去使用它。
Java选用迭代器而不是Collection来表示容器之间的共性。但是两种方法绑定到了一起,因为事先COllection就意味着提供Iterator方法。

11.13 Foreach与迭代器

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

11.13.1 适配器方法的惯用法

当希望在foreach语句中添加一种或多种foreach语句使用这个方法,可以采用适配器模式。即创造新的Iterator实现,并用适配器进行包装。

import java.util.*;

public class MultiIterableClass extends IterableClass {
  public Iterable<String> reversed() {
    return new Iterable<String>() {
      public Iterator<String> iterator() {
        return new Iterator<String>() {
          int current = words.length - 1;
          public boolean hasNext() { return current > -1; }
          public String next() { return words[current--]; }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
    };
  }	
  public Iterable<String> randomized() {
    return new Iterable<String>() {
      public Iterator<String> iterator() {
        List<String> shuffled =
          new ArrayList<String>(Arrays.asList(words));
        Collections.shuffle(shuffled, new Random(47));
        return shuffled.iterator();
      }
    };
  }	
  public static void main(String[] args) {
    MultiIterableClass mic = new MultiIterableClass();
    //直接显式调用具体的迭代器
    for(String s : mic.reversed())
      System.out.print(s + " ");
    System.out.println();
    for(String s : mic.randomized())
      System.out.print(s + " ");
    System.out.println();
    for(String s : mic)
      System.out.print(s + " ");
  }
} /* Output:
banana-shaped. be to Earth the know we how is that And
is banana-shaped. Earth that how the be And we know to
And that is how we know the Earth to be banana-shaped.
*///:~

11.13.1 注意事项–Arrays.asList

该方法将创建对原有数组的引用,因此对转化后的数据修改会直接影响原有数组。比较好的办法是创建副本。

import java.util.*;

public class ModifyingArraysAsList {
    public static void main(String[] args) {
      Random rand = new Random(47);
      Integer[] ia = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
      List<Integer> list1 =
        new ArrayList<Integer>(Arrays.asList(ia));
      System.out.println("Before shuffling: " + list1);
      Collections.shuffle(list1, rand);
      System.out.println("After shuffling: " + list1);
      System.out.println("array: " + Arrays.toString(ia));
  
      List<Integer> list2 = Arrays.asList(ia);
      System.out.println("Before shuffling: " + list2);
      Collections.shuffle(list2, rand);
      System.out.println("After shuffling: " + list2);
      System.out.println("array: " + Arrays.toString(ia));
    }
  } /* Output:
  Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  After shuffling: [4, 6, 3, 1, 8, 7, 2, 5, 10, 9]
  array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  After shuffling: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
  array: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
  *///:~

11.14 总结

Java提供了大量持有对象的方式:

  1. 数组将数字与对象关联起来。
  2. Collection保存单一的元素,而Map保存相关联的键值对。
  3. 向数组一样,List也建立了数字索引与对象的关联。
  4. 大量随机访问使用ArrayList,经常插入删除使用LinkedList。
  5. 各种Queue以及栈的行为,由LinkedList支持。
  6. Map是一种将对象与对象相关联的设计。
  7. Set不接受重复元素。
  8. 新程序中不应该使用过时的Vector、HashTable和Stack。

Java容器简图:
在这里插入图片描述

可以看到,只有四种容器:List、Set、Queue和Map。它们各有两个到三个实现版本。
点线框表示接口,实线框表示具体的类。带有空心箭头的点线表示一个特定的类实现了一个接口,实心箭头表示一个某个类可以生成箭头所指的类的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值