11.0 持有对象11.1 泛型和类型安全的容器
创建ArrayListJava SE5之前的容器的一个主要问题就是编译器允许你向容器中插入不正确的类型。使用泛型,在编译器防止将错误类型的对象放入容器 11.2 基本概念11.3 添加一组元素11.4 容器的打印
数组的打印容器类的打印 11.5 List
ArrayListlinkedList 11.6 迭代器
ListIterator 11.7 linkedList11.8 Stack11.9 Set11.10 Map11.11 Queue
PriorityQueue 11.12 Collection和Iterator
继承AbstractCollection ,需要实现size()和iterator()方法。如果继承了其他类,只能实现Collectino接口或者实现iterator() 11.13 Foreach与迭代器
适配器方法惯用法 11.14 总结
数组Collection与MapListMapSet
11.0 持有对象Java中对象在堆上创建,对象的引用和基本数据类型在栈上创建。
(1)依靠创建命名的引用来持有每一个对象
String s = "aaaa";
(2)通过数组和容器类来保存对象
Array、List、Set、Queue、Map
Array是编译器支持的类型,是保存一组对象的最有效的方式。缺点是具有固定的尺寸。
Set对于每个值都只保存一个对象。
Map是允许你将某些对象与其他一个对象关联起来的关联数组。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class A {
public static void main(String[] args) {
// 匿名内部类
List a1 = new ArrayList() {
{
add("1");
add("2");
add("3");
}
};
List a2 = new ArrayList(Arrays.asList("4","5","6")); // 11.3 有补充
List a3 = new ArrayList();
a3.add("7");
a3.add("8");
a3.add("9");
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
for (int i = 0; i < a3.size(); i++) { // size() 获取列表大小
System.out.println(a3.get(i)); // get(int index) 根据索引取值
}
}
}
Java SE5之前的容器的一个主要问题就是编译器允许你向容器中插入不正确的类型。
import java.util.ArrayList;
import java.util.List;
public class A {
@SuppressWarnings("unchecked") // 通过注解抑制 参数类型未检查 警告
public static void main(String[] args) {
List a3 = new ArrayList();
a3.add("7");
a3.add("8");
a3.add(2);
System.out.println(a3);
for (int i = 0; i < a3.size(); i++) {
System.out.println(a3.get(i));
}
}
}
使用泛型,在编译器防止将错误类型的对象放入容器
编译器会阻止你将错误类型的对象放入容器,因此变成了一个编译期错误,而不再是运行时错误。并且在将元素从List中取出时,类型转换也不再是必须的了。
import java.util.ArrayList;
import java.util.List;
public class B {
public static void main(String[] args) {
// 容器中添加泛型的子类型
List list1 = new ArrayList();
// List list1 = new linkedList(); // 使用接口的好处:当需要修改实现时只需要在创建处修改它
list1.add(new A());
list1.add(new A1());
list1.add(new A2());
for (A a : list1) {
System.out.println(a);
}
//
List list2 = new ArrayList();
list2.add(new A1());
for (A a : list2) {
System.out.println(a);
}
}
}
11.2 基本概念
Java容器类类库的作用是“保存对象”,将其划分为两个不同的概念:
(1)Collection
一个独立元素的序列,这些元素都服从一条或多条规则。
List必须按照插入的顺序保存元素,Set不能有重复元素。Queue按照队列规则来确定对象产生的顺序。
Collection可以使用add方法增加元素(当Set中元素不重复时),使用foreach遍历。
import java.util.Collection;
import java.util.HashSet;
public class B {
public static void main(String[] args) {
Collection list1 = new HashSet();
list1.add(new A1(10));
list1.add(new A1(10));
list1.add(new A1(20));
list1.add(new A2(30));
for (A a : list1) {
System.out.println(a); // A1,10 A1,10 A2,30 A1,20
}
}
}
(2)Map
一组成对的“键值对”对象,允许你使用键来查找值。ArrayList允许你使用数字来查找值,它将数字与对象关联在了一起。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”或“字典”。
public class A {
public static void main(String[] args) {
List a1 = new ArrayList(Arrays.asList(1,2,3)); // 第一种
Integer[] i = new Integer[]{1,2,3,4};
a1.addAll(Arrays.asList(1,2,3)); // 第二种
Collections.addAll(a1,1,2,3); // 第三种
Collections.addAll(a1,i); // 第三种
System.out.println(a1);
List a2 = Arrays.asList(1,2,3);
a2.set(0,6);
// a2.add(10); 报错,底层是数组
System.out.println(a2);
}
}
11.4 容器的打印
List:ArrayList、linkedList 操作多、
Set:HashSet 获取元素最快、TreeSet 比较结果的升序、linkedHashSet 按顺序添加
Map:HashMap 获取元素最快、TreeMap 比较结果的升序、linkedHashMap 按顺序添加
int[] a = new int[]{1,2,3,4,5};
System.out.println(Arrays.toString(a)); // 打印数组
System.out.println(a); // 数组的地址
容器类的打印
容器提供了toString()方法,因此可以直接打印。
打印结果:
[1, 2, 3]
{a=1, b=2}
Map11.5 Lista = new HashMap(); a.put("a", "b"); // 存键值对 System.out.println(a.get("a")); // 获取值
List是一种可修改的序列
ArrayList擅长随机访问中间插入和移除元素比较慢 linkedList
在中间插入和删除较快,提供了优化的顺序访问随机访问慢
package com.fei.hanha;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class C {
public static void main(String[] args) {
ArrayList a = new ArrayList<>(Arrays.asList(1,2,3));
a.add(5);
a.add(3,4);
ArrayList a1 = new ArrayList<>(Arrays.asList(6));
a.addAll(a1);
a.addAll(0,a1);
System.out.println(a);
a.remove(0);
ArrayList a2 = new ArrayList<>(Arrays.asList(1));
a.removeAll(a2);
a.retainAll(a2); // 只保留子集中的值
System.out.println(a);
// a.clear();
a.set(0,12);
System.out.println(a);
System.out.println(a.get(0));
System.out.println(a.subList(0,3));
System.out.println(a.contains(12));
System.out.println(a.containsAll(a1));
System.out.println("equals:" + a.equals(a1)); // 两个List的元素和长度是否相同
Collections.sort(a);
System.out.println(a);
Collections.shuffle(a);
System.out.println(a);
System.out.println(a.isEmpty());
Object[] objects = a.toArray();
System.out.println(Arrays.toString(objects));
}
}
11.6 迭代器
使用容器,必须对容器的确切类型编程。如果本来是对着List编程(使用for循环size(),get()方法遍历),但是后来向把代码应用于Set会十分不方便(Set没有get()方法)。如何编写代码能够应用于不同类型的容器?
迭代器(一种设计模式)是一个轻量级对象,创建代价小,它的工作是遍历并选择序列中的对象,只能单向移动。
如果只是向前遍历,而不修改的话,可以使用foreach(本质也是使用迭代器)。
package com.fei.hanhan;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class A {
public static void main(String[] args) {
ArrayList a1 = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7,8,9,10));
Iterator it= a1.iterator(); // (1)妖气容器返回一个迭代器(迭代器是一个对象)
while (it.hasNext()) { // 检查序列中是否还有元素
int i = it.next(); // 获取序列的下一个元素
if (i % 2 == 0) {
it.remove(); // 移除由next产生的最后一个元素。再调用之前必须先调用next()
}
}
//
for (int i = 0; i < a1.size(); i++) {
if (a1.get(i) == 3){
a1.remove(i);
// a1.set(i,99);
break;
}
}
for (int i : a1) { // 只是遍历,不修改
System.out.println("for-each:" + i);
}
}
}
package com.fei.hanhan;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;
public class C {
public static void display(Iterator it) {
while (it.hasNext()) {
System.out.print(it.next());
}
System.out.println();
}
public static void main(String[] args) {
ArrayList a1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
HashSet a2 = new HashSet<>(a1);
TreeSet a3 = new TreeSet<>(a1);
C.display(a1.iterator());
C.display(a2.iterator());
C.display(a3.iterator());
}
}
ListIterator
ListIterator是一个更加强大的Iterator子类型,它只能用于各种List类的访问,并且可以双向移动,以及赋值。
package com.fei.hanhan;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;
public class D {
public static void main(String[] args) {
ArrayList a1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7));
ListIterator l1 = a1.listIterator(); // 获取listIterator
System.out.println(l1.nextIndex() + " , " + l1.previousIndex()); // 初始值为0和-1
System.out.println("-----------");
while (l1.hasNext()) {
// 当前索引和前一个索引
System.out.println(l1.next() + " , " + l1.nextIndex() + " , " + l1.previousIndex());
}
System.out.println("-----------");
// 反向遍历
while (l1.hasPrevious()) {
System.out.print(l1.previous() + " , ");
}
System.out.println();
System.out.println("-----------");
while (l1.hasNext()) {
if (l1.next() % 2 == 0) {
l1.set(999); // 赋值
}
}
System.out.println(a1);
}
}
11.7 linkedList
在中间插入删除更高效、但是随机访问逊色一些。它还添加了一些可以使其用作栈、队列、双端队列的方法。
package com.fei.hanhanh;
import java.util.Arrays;
import java.util.linkedList;
public interface B {
public static void main(String[] args) {
linkedList a = new linkedList<>(Arrays.asList(1,2,3,4,5,6));
System.out.println(a.getFirst()); // 返回第一个元素,为空抛出异常
System.out.println(a.element()); // 返回第一个元素,为空抛出异常
System.out.println(a.peek()); // 返回第一个元素,为空null
System.out.println(a.remove()); // 移除并返回第一个元素,为空抛出异常
System.out.println(a.removeFirst()); // 移除并返回第一个元素,为空抛出异常
System.out.println(a.poll()); // 移除并返回第一个元素,为空抛出异常
a.addFirst(100); // 最前面增加
System.out.println(a);
System.out.println(a.getLast());
a.removeLast(); // 移除并返回最后元素,为空抛出异常
a.add(10); // 最后面增加
System.out.println(a);
a.addLast(100); // 最后面增加
System.out.println(a);
}
}
11.8 Stack
栈——后进先出(LIFO)的容器,有时也被称为叠加栈。
linkedList具有能够实现栈的所有功能的方法,因此可以直接将linkedList作为栈使用。
java.util.Stack是Java中定义的Stack,与下文中自己定义的Stack具有相同的方法。
import java.util.linkedList; public class Stack{ private linkedList storage = new linkedList (); public void push(T t) { storage.addFirst(t); } 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 C {
public static void main(String[] args) {
Stack stack = new Stack();
String s = "My name is Bob";
for (String s1 : s.split(" ")) {
stack.push(s1);
}
while (!stack.empty()) {
System.out.println(stack.pop());
}
}
}
11.9 Set
Set不保存重复的元素。
HashSet使用散列函数,TreeSe将元素存储在红黑数数据结构中。linkedHashSet也使用散列。
public class D {
public static void main(String[] args) {
Set objects = new linkedHashSet<>();
Random random = new Random(50);
for (int i = 0; i < 1000; i++) {
objects.add(random.nextInt(30));
}
System.out.println(objects);
}
}
Set使用最多的是contains()
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class E {
public static void main(String[] args) {
Set objects = new HashSet<>(Arrays.asList(1,2,3,4,5));
List i = Arrays.asList(8, 9);
System.out.println(objects);
System.out.println(objects.contains(6));
System.out.println(objects.contains(5));
System.out.println(objects.containsAll(i));
objects.addAll(i);
System.out.println(objects);
objects.removeAll(i);
System.out.println(objects);
}
}
向TreeSet传入一个比较器,使得按字母排序(不会根据字母大小写分组)
package com.fei.hanhanh;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
public class E {
public static void main(String[] args) {
Set words = new TreeSet(String.CASE_INSENSITIVE_ORDER);
List list = Arrays.asList("b", "C", "d", "A", "z", "M");
words.addAll(list);
System.out.println(words);
}
}
11.10 Map
用Map检查Random函数的随机性是否是正态分布的。
key是随机值,value是出现次数。
package com.fei.hanhanh;
import java.util.HashMap;
import java.util.Random;
public class F {
public static void main(String[] args) {
HashMap i = new HashMap<>();
Random random = new Random(27);
for (int j = 0; j < 1000; j++) {
int num = random.nextInt(10);
Integer value = i.get(num);
i.put(num,value == null ? 1 : value + 1);
}
System.out.println(i);
}
}
根据key和value在Map中查找
import java.util.HashMap;
public class F {
public static void main(String[] args) {
HashMap i = new HashMap<>();
i.put("A",new A());
i.put("B",new B());
C c = new C();
i.put("C",c);
System.out.println(i.containsKey("A"));
System.out.println(i.containsValue(c));
System.out.println(i.get("A"));
}
}
将多个容器组合起来
import com.fei.han.Animal;
import com.fei.han.Person;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MC {
public static void main(String[] args) {
Map> personListMap = new HashMap<>();
personListMap.put(new Person(), Arrays.asList(new Animal(),new Animal()));
personListMap.put(new Person(), Arrays.asList(new Animal(),new Animal()));
personListMap.put(new Person(), Arrays.asList(new Animal(),new Animal()));
System.out.println(personListMap);
System.out.println(personListMap.keySet());
System.out.println(personListMap.values());
for ( Person p :personListMap.keySet()) {
System.out.print(p + " ");
for (Animal animal : personListMap.get(p)) {
System.out.print(animal + " ");
}
System.out.println();
}
}
}
11.11 Queue
队列是一个典型的先进先出的容器。队列常被当作一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要,因为它们可以安全地将对象从一个任务传输给另一个任务。
linkedList实现了Queue接口。
import java.util.linkedList;
import java.util.Queue;
import java.util.Random;
public class QueueDemo {
public static void main(String[] args) {
Queue queue = new linkedList();
Random random = new Random();
for (int i = 0; i < 20; i++) {
queue.offer(random.nextInt(10));
}
System.out.println(queue);
Queue queue2 = new linkedList();
for (char character : "aabbccdd".toCharArray()) {
queue2.offer(character);
}
System.out.println(queue2);
}
}
PriorityQueue
优先级队列:声明下一个弹出元素是最需要的元素。
当调用offer()方法插入一个对象时,这个对象会被排序(默认使用自然顺序,也可以使用自己的比较器,如Collections.reverseOrder())。
当弹出元素时,获取的将会是优先级最高的元素。
PriorityQueue经常与Integer、String、Character这些内置类型工作。
最小的值拥有最高的优先级(字符串空格>大写字母>小写字母>其他字符)
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Random;
public class PPP {
public static void main(String[] args) {
PriorityQueue objects = new PriorityQueue();
Random random = new Random(27);
for (int i = 0; i < 10; i++) {
objects.offer(random.nextInt(50));
}
//System.out.println(objects);
while (objects.peek() != null) {
System.out.print(objects.remove() + " "); // 值越小优先级越高
}
List i1 = Arrays.asList(11, 2, 5, 21, 99);
PriorityQueue objects1 = new PriorityQueue(i1.size(), Collections.reverseOrder());
objects1.addAll(i1);
System.out.println();
System.out.println(objects1);
while (objects1.peek() != null) {
System.out.print(objects1.remove() + " "); // 值越小优先级越高
}
List list1 = Arrays.asList("Hello Word!".split(""));
PriorityQueue objects2 = new PriorityQueue(list1);
System.out.println(objects2);
while (objects2.peek() != null) {
System.out.print(objects2.remove() + " "); // 值越小优先级越高 // 空格 H W d e l l o o r !
}
}
}
11.12 Collection和Iterator
Collection是所有序列容器的根接口。java.util.AbstractCollection是Collection的默认实现,可以继承它创建子类型。
继承AbstractCollection ,需要实现size()和iterator()方法。package com.fei.hanhanha;
import com.fei.hanh.A;
import com.sun.glass.ui.Size;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
public class AAAA extends AbstractCollection {
private PPP[] ppp = new PPP[]{new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP()};
public int size() {
return ppp.length;
}
public Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
return index < ppp.length;
}
public PPP next() {
return ppp[index++];
}
};
}
public static void main(String[] args) {
AAAA aaaa = new AAAA();
Iterator it = aaaa.iterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
Collection p = aaaa;
System.out.println();
for (PPP ppp : p) {
System.out.print(ppp + " ");
}
}
}
如果继承了其他类,只能实现Collectino接口或者实现iterator()
package com.fei.hanhanha;
import java.util.Collection;
import java.util.Iterator;
public class BBB {
private PPP[] ppp = new PPP[]{new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP()};
public Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
return index < ppp.length;
}
public PPP next() {
return ppp[index++];
}
};
}
public static void main(String[] args) {
BBB aaaa = new BBB();
Iterator it = aaaa.iterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
}
}
11.13 Foreach与迭代器
foreach用于数组、Collection对象以及实现了Iterable接口的类。
package com.fei.hanhanha; import java.util.Iterator; public class CCC implements Iterable{ private PPP[] ppp = new PPP[]{new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP(),new PPP()}; public Iterator iterator() { return new Iterator () { private int index = 0; public boolean hasNext() { return index < ppp.length; } public PPP next() { return ppp[index++]; } }; } public static void main(String[] args) { CCC ccc = new CCC(); for (PPP ppp : ccc) { System.out.print( ppp + " " ); } } }
大量的类实现了Iterable接口,如所有的Collection类(除了Map)
package com.fei.hanhanha;
import java.util.Map;
public interface ZZZ {
public static void main(String[] args) {
// 获取操作系统环境变量 将Map变为一个由Map.Entry组成的Set
System.out.println(System.getenv().entrySet());
for (Map.Entry entry : System.getenv().entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
适配器方法惯用法
DDD是一个Iterable类,它的Iterable方法是按顺序迭代的。如果想实现反向迭代,可以通过覆写iterable方法,但是顺序迭代的方法就不能用了。如果想保留两个方法,可以使用适配器方法。即不能直接覆盖,而是添加一个能够产生Iterable对象的方法,该方法可用于foreach。
package com.fei.hanhanha; import com.sun.org.apache.bcel.internal.generic.RETURN; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; public class DDD implements Iterable11.14 总结 数组{ private Integer[] ppp = new Integer[]{new Integer(6),new Integer(3),new Integer(19),new Integer(1)}; public Iterator iterator() { return new Iterator () { private int index = 0; public boolean hasNext() { return index < ppp.length; } public Integer next() { return ppp[index++]; } }; } public Iterable reversed() { return new Iterable () { public Iterator iterator() { return new Iterator () { private int index = ppp.length - 1; public boolean hasNext() { return index > -1; } public Integer next() { return ppp[index--]; } }; } }; } public Iterable randomized() { return new Iterable () { public Iterator iterator() { List strings = new ArrayList (Arrays.asList(8,7,10,1)); Collections.shuffle(strings,new Random(20)); return strings.iterator(); } }; } public static void main(String[] args) { DDD ddd = new DDD(); for (Integer ppp : ddd.reversed()) { System.out.print( ppp + " " ); } System.out.println(); for (Integer ppp : ddd.randomized()) { System.out.print( ppp + " " ); } } }
(1)将数字与对象联系起来(2)保存类型明确的对象,查询对象时,不需要对结果做类型转换(3)可以是多维的,可以保存基本数据类型(4)一旦生成,容量就不能改变
Collection与Map(1)Collection保存单一元素,Map保存键值对(2)使用泛型可以指定容器中对象的类型,取出时不必进行类型转换(3)添加时可以自动调整尺寸(4)不能持有基本类型,但是自动包装机制会执行基本类型到包装器类型的转换。
List(1)数组和List建立了数字索引与对象的关联,都是排序的,List能自动扩容。
(2)随机访问用ArrayList,从表中间插入删除用linkedList。
(3)linkedList支持栈和队列的行为。
(1)HashMap用于快速访问,TreeMap保持键的有序,linkedHashMap保持插入的顺序,但也提供快速访问。
Set(1)HashSet查询最快,TreeSet保持元素有序,linkedHashSet保持插入的顺序。



