第十七章 容器深入研究

1、完整容器分类法

2、填充容器

Collections类也有一些实用的static方法,其中包括fill,同Arrays一样只复制同一对象引用来填充整个容器的,并且只对List对象有用,但是所产生的列表可以传递给构造器或addAll方法:

class StringAddress {
  private String s;
  public StringAddress(String s) {
    this.s = s;
  }
  @Override
  public String toString() {
    return super.toString() + " " + s;
  }
}

public class FillingLists {
  public static void main(String[] args) {
    List<StringAddress> list= new ArrayList<StringAddress>(
      Collections.nCopies(4, new StringAddress("Hello")));
    System.out.println(list);
    Collections.fill(list, new StringAddress("World!"));
    System.out.println(list);
  }
} /*
[StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello]
[StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!]
*/

第一种是使用Collections.nCopies创建传递给构造器的List。所有引用都被设置为指向相同的对象,Collection.fill被调用之后也是如此,fill方法只能替换已经在List中存在的元素,而不能添加新的元素

2.1 一种Generator解决方案

所有的Collection子类型都有一个接收另一个Collection对象的构造器,用所接收的Collection对象中的元素来填充新的容器。addAll是所有Collection子类型的一部分,可以用来组装现有的Collection。 
构建接收Generator和quantity数值并将它们做为构造器参数的类:

public class CollectionData<T> extends ArrayList<T> {
    public CollectionData(Generator<T> gen, int quantity) {
        for(int i = 0; i < quantity; ++i) {
            this.add(gen.next());
        }

    }

    public static <T> CollectionData<T> list(Generator<T> gen, int quantity) {
        return new CollectionData(gen, quantity);
    }
}

2.2 map生成器

可以使用工具来创建任何用于Map或Collection的生成数据集,然后通过构造器或Map.putAll和Collection.addAll方法来初始化Map和Collection。

2.3 使用Abstract类

享元模式:运用共享技术有效来支持大量细粒度对象的复用,他通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相类似的开销,从而提高系统资源利用率。

主要优点:相同对象只需要保存一份,降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的的压力。

缺点:①为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序复杂性;②读取享元模式的外部状态会使运行时间稍微变长;

3、Collection的功能方法

è¿éåå¾çæè¿°

 4、可选操作

执行各种不同的添加和移除的方法在Collection接口中都是可选操作。这意味着实现类并不需要为这些方法提供功能定义。 
可选操作声明调用某些方法将不会执行有意义的行为,相反,它们会抛出异常。如果一个操作是可选的,编译器仍旧会严格要求你只能调用该接口中的方法。

4.1 未获支持操作

最常见的未获支持的操作,源于背后由固定尺寸的数据结构支持的容器。当使用Arrays.asList将数组转换为List时,就会得到这样的容器。还可以通过使用Collections类中不可修改的方法,选择创建任何会抛出UnsupportedOperationException的容器:

public class Unsupported {
  static void test(String msg, List<String> list) {
    System.out.println("--- " + msg + " ---");
    Collection<String> c = list;
    Collection<String> subList = list.subList(1,8);
    // 复制列表
    Collection<String> c2 = new ArrayList<String>(subList);
    try { c.retainAll(c2); } catch(Exception e) {
      System.out.println("retainAll(): " + e);
    }
    try { c.removeAll(c2); } catch(Exception e) {
      System.out.println("removeAll(): " + e);
    }
    try { c.clear(); } catch(Exception e) {
      System.out.println("clear(): " + e);
    }
    try { c.add("X"); } catch(Exception e) {
      System.out.println("add(): " + e);
    }
    try { c.addAll(c2); } catch(Exception e) {
      System.out.println("addAll(): " + e);
    }
    try { c.remove("C"); } catch(Exception e) {
      System.out.println("remove(): " + e);
    }
    // set()方法只改变值
    try {
      list.set(0, "X");
    } catch(Exception e) {
      System.out.println("List.set(): " + e);
    }
  }
  public static void main(String[] args) {
    List<String> list = Arrays.asList("A B C D E F G H I J K L".split(" "));
    test("Modifiable Copy", new ArrayList<String>(list));
    test("Arrays.asList()", list);
    test("unmodifiableList()",
      Collections.unmodifiableList(new ArrayList<String>(list)));
  }
} /*
--- Modifiable Copy ---
--- Arrays.asList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
--- unmodifiableList() ---
retainAll(): java.lang.UnsupportedOperationException
removeAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
List.set(): java.lang.UnsupportedOperationException
*/

5、List的功能方法

basicTest包含每个list都可以执行的操作;iterMotion使用Iterator遍历元素;对应的iterManipulation使用Iterator修改元素;testVisual用以查看List的操作效果;


public class Lists {
  private static boolean b;
  private static String s;
  private static int i;
  private static Iterator<String> it;
  private static ListIterator<String> lit;
  public static void basicTest(List<String> a) {
    a.add(1, "x"); // 在位置1添加
    a.add("x"); // 在结尾添加
    // 添加一个集合
    a.addAll(Countries.names(25));
    // 从3开始添加一个集合
    a.addAll(3, Countries.names(25));
    b = a.contains("1"); // 是否存在
    // 整个集合都在里面吗
    b = a.containsAll(Countries.names(25));
    // 随机访问列表, ArrayList简单, LinkedList昂贵
    s = a.get(1); // Get (typed) object at location 1
    i = a.indexOf("1"); // Tell index of object
    b = a.isEmpty(); // Any elements inside?
    it = a.iterator(); // Ordinary Iterator
    lit = a.listIterator(); // ListIterator
    lit = a.listIterator(3); // Start at loc 3
    i = a.lastIndexOf("1"); // Last match
    a.remove(1); // Remove location 1
    a.remove("3"); // Remove this object
    a.set(1, "y"); // Set location 1 to "y"
    a.retainAll(Countries.names(25));
    a.removeAll(Countries.names(25));
    i = a.size(); 
    a.clear();
  }
  public static void iterMotion(List<String> a) {
    ListIterator<String> it = a.listIterator();
    b = it.hasNext();
    b = it.hasPrevious();
    s = it.next();
    i = it.nextIndex();
    s = it.previous();
    i = it.previousIndex();
  }
  public static void iterManipulation(List<String> a) {
    ListIterator<String> it = a.listIterator();
    it.add("47");
    // Must move to an element after add():
    it.next();
    // Remove the element after the newly produced one:
    it.remove();
    // Must move to an element after remove():
    it.next();
    // Change the element after the deleted one:
    it.set("47");
  }
  public static void testVisual(List<String> a) {
    print(a);
    List<String> b = Countries.names(25);
    print("b = " + b);
    a.addAll(b);
    a.addAll(b);
    print(a);
    // Insert, remove, and replace elements
    // using a ListIterator:
    ListIterator<String> x = a.listIterator(a.size()/2);
    x.add("one");
    print(a);
    print(x.next());
    x.remove();
    print(x.next());
    x.set("47");
    print(a);
    // Traverse the list backwards:
    x = a.listIterator(a.size());
    while(x.hasPrevious()) {
      printnb(x.previous() + " ");
    }
    print();
    print("testVisual finished");
  }
  // There are some things that only LinkedLists can do:
  public static void testLinkedList() {
    LinkedList<String> ll = new LinkedList<String>();
    ll.addAll(Countries.names(25));
    print(ll);
    // Treat it like a stack, pushing:
    ll.addFirst("one");
    ll.addFirst("two");
    print(ll);
    // Like "peeking" at the top of a stack:
    print(ll.getFirst());
    // Like popping a stack:
    print(ll.removeFirst());
    print(ll.removeFirst());
    // Treat it like a queue, pulling elements
    // off the tail end:
    print(ll.removeLast());
    print(ll);
  }
  public static void main(String[] args) {
    // Make and fill a new list each time:
    basicTest(
      new LinkedList<String>(Countries.names(25)));
    basicTest(
      new ArrayList<String>(Countries.names(25)));
    iterMotion(
      new LinkedList<String>(Countries.names(25)));
    iterMotion(
      new ArrayList<String>(Countries.names(25)));
    iterManipulation(
      new LinkedList<String>(Countries.names(25)));
    iterManipulation(
      new ArrayList<String>(Countries.names(25)));
    testVisual(
      new LinkedList<String>(Countries.names(25)));
    testLinkedList();
  }
}

6、Set和存储顺序

存储顺序不同的set实现不仅具有不同的行为,而且它们对于可以在特定的set中放置元素的类型也有不同的要求:

è¿éåå¾çæè¿° 

6.1 SortedSet

  • SortedSet中的元素可以保证处于排序状态,SortedSet接口中的下列方法提供附加的功能:Comparator comparator返回当前Set使用的Comparator;或者返回null,表示以自然方式排序。 
  • Object first返回容器中的第一个元素。 
  • Object last返回容器中的最末一个元素。 
  • SortedSet subSet(fromElement, toElement)生成此Set的子集,范围从fromElement(包含)到toElement(不包含)。 
  • SortedSet headSet(toElement)生成此Set的子集,由小于toElement的元素组成 
  • SortedSet tailSet(fromElement)生成此Set的子集,由大于或等于fromElement的元素组成

7、队列

除了并发应用,Queue在Java SE5中仅有的两个实现是LinkedList和PriorityQueue,它们的差异在于排序行为而不是性能。

public class QueueBehavior {
  private static int count = 10;
  static <T> void test(Queue<T> queue, Generator<T> gen) {
    for(int i = 0; i < count; i++) {
      queue.offer(gen.next());
    }
    while(queue.peek() != null) {
      System.out.print(queue.remove() + " ");
    }
    System.out.println();
  }
  static class Gen implements Generator<String> {
    String[] s = ("one two three four five six seven eight nine ten").split(" ");
    int i;
    @Override
    public String next() {
      return s[i++];
    }
  }
  public static void main(String[] args) {
    test(new LinkedList<String>(), new Gen());
    test(new PriorityQueue<String>(), new Gen());
    test(new ArrayBlockingQueue<String>(count), new Gen());
    test(new ConcurrentLinkedQueue<String>(), new Gen());
    test(new LinkedBlockingQueue<String>(), new Gen());
    test(new PriorityBlockingQueue<String>(), new Gen());
  }
} /*
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
one two three four five six seven eight nine ten
eight five four nine one seven six ten three two
*/

7.1 优先队列

class ToDoList extends PriorityQueue<ToDoList.ToDoItem> {
  static class ToDoItem implements Comparable<ToDoItem> {
    private char primary;
    private int secondary;
    private String item;
    public ToDoItem(String td, char pri, int sec) {
      primary = pri;
      secondary = sec;
      item = td;
    }
    @Override
    public int compareTo(ToDoItem arg) {
      if(primary > arg.primary) {
        return +1;
      }
      if(primary == arg.primary) {
        if (secondary > arg.secondary) {
          return +1;
        } else if (secondary == arg.secondary) {
          return 0;
        }
      }
      return -1;
    }
    @Override
    public String toString() {
      return Character.toString(primary) +
        secondary + ": " + item;
    }
  }
  public void add(String td, char pri, int sec) {
    super.add(new ToDoItem(td, pri, sec));
  }
  public static void main(String[] args) {
    ToDoList toDoList = new ToDoList();
    toDoList.add("Empty trash", 'C', 4);
    toDoList.add("Feed dog", 'A', 2);
    toDoList.add("Feed bird", 'B', 7);
    toDoList.add("Mow lawn", 'C', 3);
    toDoList.add("Water lawn", 'A', 1);
    toDoList.add("Feed cat", 'B', 1);
    while(!toDoList.isEmpty()) {
      System.out.println(toDoList.remove());
    }
  }
} /*
A1: Water lawn
A2: Feed dog
B1: Feed cat
B7: Feed bird
C3: Mow lawn
C4: Empty trash
*/

 7.2 双向队列

public class Deque<T> {
    private LinkedList<T> deque = new LinkedList();

    public Deque() {
    }

    public void addFirst(T e) {
        this.deque.addFirst(e);
    }

    public void addLast(T e) {
        this.deque.addLast(e);
    }

    public T getFirst() {
        return this.deque.getFirst();
    }

    public T getLast() {
        return this.deque.getLast();
    }

    public T removeFirst() {
        return this.deque.removeFirst();
    }

    public T removeLast() {
        return this.deque.removeLast();
    }

    public int size() {
        return this.deque.size();
    }

    public String toString() {
        return this.deque.toString();
    }
}
public class DequeTest {
  static void fillTest(Deque<Integer> deque) {
    for(int i = 20; i < 27; i++) {
      deque.addFirst(i);
    }
    for(int i = 50; i < 55; i++) {
      deque.addLast(i);
    }
  }
  public static void main(String[] args) {
    Deque<Integer> di = new Deque<Integer>();
    fillTest(di);
    print(di);
    while(di.size() != 0) {
      printnb(di.removeFirst() + " ");
    }
    print();
    fillTest(di);
    while(di.size() != 0) {
      printnb(di.removeLast() + " ");
    }
  }
} /*
[26, 25, 24, 23, 22, 21, 20, 50, 51, 52, 53, 54]
26 25 24 23 22 21 20 50 51 52 53 54
54 53 52 51 50 20 21 22 23 24 25 26
*/

 8、Map

8.1 性能

运用hashcode()可以显著提高性能;


HashMap*(默认)         Map基于散列表实现(它取代了HashTable)。插入和查询“键值对”的开销是固定,可以通过构造器设                                              置容量和负载因子,以调整容器的性能。

LinkedHashMap              类似于HashMap,但是迭代遍历时,取得“键值对”的顺序是插入顺序,或者是最少使用(LRU)的次                                                序,只比HashMap慢一点;而在迭代访问时反而更快,因为它使用链表维护内部次序。

TreeMap                            基于红黑树的实现。查看“键”或“键值对”时,它会被排序(次序由Comparable或Comparator实现)。                                              TreeMap的特点在于,所得到的结果是经过排序的。TreeMap是唯一带有subMap()方法的Map,它可                                                以返回一个子树。

WeakHashMap               弱键映射,允许释放映射所指向的对象,这是为解决某类特殊问题而设计的。如果映射之外没有引用                                                指向某个“键”可以被垃圾收集器回收。

ConcurrentHashMap     一种线程安全的Map,它不涉及同步加锁,在并发中常用。 

IdentityHashMap            使用==代替equals对“键”进行比较散列映射。专门为解决特殊问题而设计。


对Map中使用键的要求与对Set中的元素的要求一样。任何键都必须具有一个equals()方法,如果键被用于散列Map那么必须还具有恰当的hashCode()方法,如果键被用于TreeMap,必须实现Comparable。

8.2 SortedMap
使用SortedMap可以确保键处于排序状态,这使得它具有额外的功能,这些功能由SortedMap接口中的方法提供。

8.3 LinkedHashMap
为了提高速度,LinkeHashMap散列化所有的元素,但是在遍历键值对时,却又以元素插入顺序返回键值对。此外,可以在构造器中设定LinkedHashMap,使之采用基于访问的最少使用算法,于是需要定期清理元素以节省空间的程序来说,此功能使得程序很容易实现。

9、散列与散列码

在使用自己的类作为HashMap的键,必须重载hashCode()和equals(),默认的Object.equals()只是比较对象的地址。Object.hashCode()默认是使用对象的地址计算散列码。你必须同时重载hashCode()和equals(),HashMap使用equals()判断当前的键是否与表中存在的键相同。

正确的equals()方法必须满足5个条件: 
自反性、对称性、传递性、一致性、对任何不是null的x,x.equals()一定返回false。

9.1 计算散列码 

————————————————————————————————————————————————————

                                          域类型                                                                                         计算

————————————————————————————————————————————————————

                        Boolean                                                                                     c = (f ? 0:1)

                        byte、char、short、int                                                           c = (int)f

                        long                                                                                            c = (int)(f ^ (f >>>32))

                        float                                                                                            c =  Float.floatToIntBits(f);

                        Double                                                                                      long l = Double.doubleToLongBits(f);

                                                                                                                           c = (int) (l ^ ( l >>> 32)

                        Object,其equals调用这个域的equals                                 c =   f.hashCode()

                         数组                                                                                          对每个元素应用上述规则

————————————————————————————————————————————————————

10、容器的选择

  • 对List的选择
  • 对Set的选择
  • 对Map的选择

11、实用方法

12、持有引用

参见:https://blog.csdn.net/hou_shiyu/article/details/99689150

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值