Java 集合框架
Java 的集合框架将一些常用的数据结构封装成类,相当于 C++ STL 库中实现的抽象数据类型。其中声明了对各种集合类型执行的一般操作。如下定义了实现这些抽象数据类型的接口及它们的继承关系,包括 Set, List, Queue, Map, SortedSet, SortedMap 等,继承关系如下。
Collection 是 Java 定义的一个接口,实现该接口的是一个抽象类 AbstractCollection,通过继承关系,其子类陆续实现了一些抽象数据结构,如下所示。
Collection 接口中定义了一些常用方法,包括:
- int size() —— 返回集合对象中包含的元素个数
- boolean isEmpty() —— 判断集合对象中是否还包含元素,如果没有任何元素,则返回 true
- boolean contains (Object obj) —— 判断对象是否在集合里面
- boolean containsAll (Collection c) —— 判断方法的接受者对象是否包含集合中的所有元素
- boolean add (Object obj) —— 向集合中增加对象
- boolean addAll (Collection<?> c) —— 将参数集合中的所有元素增加到接收者集合中
- boolean remove (Object obj) —— 从集合中删除对象
- boolean removeAll (Collection c) —— 将参数集合中的所有元素从接收者集合中删除
- boolean retainAll (Collection c) —— 在接收者集合中保留参数集合中的所有元素,其它的元素都删除。
- void clear() —— 删除集合中的所有元素。
Set 接口
Set 为集合接口,其中的 equals() 方法重写了 Object 类中的 equals() 方法。Object 类中的 equals() 方法是当比较的对象是同一个时,而不是内容相同才返回 true。而 Set 类中的 equals() 方法是判断两个对象的内容是否相同。
实现 Set 接口的类
基础的类包括 Hashset, TreeSet
其它实现的类和其子类:
- AbstractSet
- ConcurrentSkipListSet (基于跳表)
- CopyOnWriteArraySet
- EnumSet
- JobStateReasons
- LinkedHashset
SortedSet 接口
SortedSet 接口中的元素是升序排列的(C++ 中的 set 类本身是排好序的,相当于实现了这里的 SortedSet 接口),而且增加了和次序有关的操作,常用语存放词汇表这样的内容。实现它的类包括:ConcurrentSkipListSet, TreeSet。
List 接口
List 接口中可包含重复的元素,而且每个元素是有顺序的,每个元素都有一个 index 值(从 0 开始)标明元素在列表中的位置。
实现 List 接口的类
- Vector:动态数组,线程安全,但是现在不太使用了(随机访问较为慢)
- ArrayList:相当于实现数组的类,但是是动态数组,随机访问速度极快,非线程安全
- LinkedList:内部实现是链表,适合于需要频繁插入和删除操作的应用场景
- Stack:后进先出
- 其它:AbstractList, AbstractSequentialList, AttributeList, CopyOnWriteArrayList, RoleList, RoleUnresolvedList
Queue 接口
先进先出(FIFO)
实现 Queue 接口的类
- LinkedList:先进先出的 List,链表实现
- PriorityQueue:优先队列,实现了堆
- 其它:AbstractQueue, ArrayBlockingQueue, ArrayDeque(双端队列), ConcurrentLinkedQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue
Map 接口
Map 用于维护键值对 (Key - Value),其中键的值不能重复,键和值的类型可以不同,所以泛型的传入类型有连个,分别为 Key 和 Value。
以下是 Map 接口和它的子接口和实现:
实现 Map 接口的类
- HashMap
- TreeMap
- 其它:AbstractMap, Attributes, AuthProvider, ConcurrentHashMap, ConcurrentSkipListMap(基于跳表), EnumMap, Hashtable, IdentityHashMap, LinkedHashMap, PrinterStateReasons, Properties, Provider, RenderingHints, SimpleBindings, TabularDataSupprot, UIDefaults, WeakHashMap
SortedMap 接口
是 Map 的子接口,其中的关键字是升序排列的(C++ 中的 map 类实现时其实就将关键字排序了)。
实现的接口
- TreeMap
- ConcurrentSkipListMap(支持并发)
算法
大多用于 List 对象,对标 C++ STL 中的 algorithm。其中的 min() 和 max() 适用于任何的实现了 Collection 接口的对象。
sort
和 C++ 中的 sort 差不多
- 默认将 List 元素按照升序排列
- 简单形式下将基本数据类型的元素按照自然次序排列,或者是元素的类实现了 Comparable 接口
- 第二种形式是附加一个 Comparator 对象作为参数,可以自己规定比较的规则, 可以用于实现反序或者特殊次序的排序。
- 复杂度为 nlog(n),稳定。
shuffle
用于将 List 中的元素打乱,采用随机的方式打乱。
其它常用算法
- reverse():将一个 List 中的元素反向排列
- fill():将某个值覆盖 List 中的每一个元素, 常常用于 List 的初始化
- copy():接受源 List 和目标 List,将源中的元素复制到目标,覆写其中的内容,目标 List 的长度必须不得小于原 List,如果目标 List 更长,多余部分的内容保留原样。
二分查找,binarySearch()
- 在一个有序的 List 中查找指定的元素,List 中的元素必须已经排好序了。
- 和 sort() 一样,可以按照自然升序规则查找,也可以传入一个 Comparator 对象,自行设定比较规则,并假定 List 就是按照这种规则排序的。
- 容器必须是实现了 RandomAccess 接口的,否则只能线性查找。
min() 和 max()
可以用于任何集合对象,min() 返回最小值,max() 返回最大值。同样默认按照自然顺序规则计算返回,也可以自己定义 Comparator 对象自己定义比较规则。
数组的实用方法
Arrays 类
- 在 java.util.Arrays 包中
- 常用方法
- fill (type[] a, type val):将 a 的所有数据填充为 val。
- equals (type[] a, type[] b):比较是否内容相等,而不是比较是否同一。
- sort (type[] a):排序,如前文介绍
- binarySearch ():二分查找
- asList (T… a):将数组转化为 ArrayList 类型
- toString (数组的引用):实现到 String 的转换
测试数组的复制和填充
import java.util.Arrays;
public static void copyArrays(){
int[] a = new int[6];
int[] b = new int[10];
Arrays.fill(a, 47);
Arrays.fill(b, 99);
for(int i: a){
System.out.print(i+"\t");
}
System.out.println();
for(int i: b){
System.out.print(i+"\t");
}
System.out.println();
System.arraycopy(a, 0, b, 0, a.length);
for(int i: b){
System.out.print(i+"\t");
}
System.out.println();
int[] c = new int[5];
Arrays.fill(c, 103);
System.arraycopy(c, 0, a, 0, c.length);
for(int i: a){
System.out.print(i+"\t");
}
System.out.println();
Integer[] u = new Integer[5];
Integer[] v = new Integer[14];
Arrays.fill(u, new Integer(34));
Arrays.fill(v, new Integer(50));
System.arraycopy(u, 0, v, 5, u.length);
for(Integer i: v){
System.out.print(i+"\t");
}
System.out.println();
}
测试的结果如下:
47 47 47 47 47 47
99 99 99 99 99 99 99 99 99 99
47 47 47 47 47 47 99 99 99 99
103 103 103 103 103 47
50 50 50 50 50 34 34 34 34 34 50 50 50 50
测试数组的比较
import java.util.Arrays;
public static void compareArrays(){
int[] a1 = new int[10];
int[] a2 = new int[10];
Arrays.fill(a1, 7);
Arrays.fill(a2, 7);
System.out.println(Arrays.equals(a1, a2));
a2[3] = 11;
System.out.println(Arrays.equals(a1, a2));
String[] s1 = new String[5];
Arrays.fill(s1, "Hi");
String[] s2 = {"Hi", "Hi", "Hi", "Hi", "Hi"};
System.out.println(Arrays.equals(s1, s2));
}
结果如下:
true
false
true
Vector 和 ArrayList
- 两者都能够存储相同类型的对象,或者是有相同父类的对象,或者实现了公共接口的对象。
- 两者都不能存储基本数据类型的数据,而是要将基本数据类型包裹到类里面才能存储到 Vector 或者 ArrayList 中。如 int 类型要包裹到 Integer 类型中才能存储。
- 两者都是动态数组,容量都可以自动扩充
- 添加元素的效率比较高;当空间已满,需要扩充的时候例外
- Vector 是集合框架中的遗留类,旧线程安全
- ArrayList 方法是非同步的,效率较高
- 现在的 Java 提供了线程安全集合:Java.util.concurrent 包,所以现在 Vector 已经几乎被淘汰了
- 任何集合类都可以使用同步包装器使其变为线程安全的。
List <E> synchArrayList = Collections.synchornisedList (new ArrayList <E>());
ArrayList 的构造方法
- ArrayList():构造一个空表,长度默认为10
- ArrayLsit (Collection<? extends E> c):用 c 中的元素为初始值构造一个表
- ArrayList (int initialCapacity):创建一个空表,长度为 initialCapacity
ArrayList 的其它方法
遍历 Collection
通过 Enumeration 以及 Iterator 接口
- Java 中的许多方法(如 elements() )都是返回 Enumeration 类型的对象,而不是返回 Collection 对象或者 其中元素的对象
- Enumeration 接口不能用于 ArrayList 对象,而 Iterator 接口既可以用于 ArrayList 对象也可以用于 Vector 对象。
- Iterator 接口是对 Enumeration 接口的改进,所以优先使用 Iterator 接口。
Iterator
- 具有去除 Collection 对象中元素的能力
- hasNext():判断后面是否还有元素
- next():取得下一个元素
- remove():去除下一个 Collection 对象中的元素
举例如下:
import java.util.Vector;
import java.util.Iterator;
public static void iteratorTester(){
String[] num = {"one", "two", "three", "four", "five",
"six", "seven", "eight", "nine", "ten"};
// 使用 asList() 将num包装成 ArrayList 类型
Vector<String> avec = new Vector<>(java.util.Arrays.asList(num));
System.out.println("Before Vector: " + avec);
Iterator<String> it = avec.iterator();
while(it.hasNext()){
String s = (String)it.next();
System.out.println(s);
if(s.length()>4) it.remove();
}
System.out.println("After Vector: " + avec);
}
结果如下:
Before Vector: [one, two, three, four, five, six, seven, eight, nine, ten]
one
two
three
four
five
six
seven
eight
nine
ten
After Vector: [one, two, four, five, six, nine, ten]
使用增强的 for 循环
举例如下:
public static void forTester(){
Vector<String > dayNames = new Vector();
dayNames.add("Sunday"); dayNames.add("Monday");
dayNames.add("Tuesday"); dayNames.add("Wednesday");
dayNames.add("Thursday"); dayNames.add("Friday");
dayNames.add("Saturday");
for(String day: dayNames){
System.out.println(day);
}
}
使用聚集函数和 lambda 表达式
Map 接口及其实现
Map 存储 key - value 类型的数据,通过 key 可以实现对值得快速存取和使用
有两个主要实现的类,包括 HashTable(java 1.0) 和HashMap(java 1.2)
Map 接口的查询方法
- int size():返回 Map 中的元素个数
- boolean isEmpty():如果为空,则返回 false,否则返回 true
- boolean containsKey (Object key): 判断给定的参数是否为 Map 中的一个 key
- boolean containsValue (Object key):判断给定参数是否为 Map 中的一个 value
- Object get (Object key):返回关键字对应的值
- Collection values():返回包含 Map 中所有的 value 组成的 Collection 对象
- Set keySet():返回包含 Map 中所有 key 的 Set 对象
- Set entrySet():返回包含 Map 中所有项的 Set 对象
Map 接口额修改方法
- Object put (Object key, Object value):将给定的 key-value 加入到 Map 对象中。如果 key 在 Map 中已经存在,则原来的 value 会被新的 value 覆盖
- void putAll (Map m):将参数 Map 对象中的项加入到调用的 Map 对象中去
- Object remove (Object key):将关键字为 key 的项删除
- void clear():将 Map 清空
Map 的一些基本概念
- Map 中键的查找使用的是哈希函数
- Map 的容量不是固定的,而是随着对象的加入可以自动扩充的
- item(项):一个 key-value 对,其中双方都不能为空
- load factor(装填因子):(表中已有的项数) / (表的容量)
HashMap 构造方法
HashMap 的其它方法