数组是一种很常见的数据结构,开始接触编程的时候多数程序都和数组相关。刚开始接触Java时也是一直使用数组写一些程序,后来越来越觉得...
数组是一种很常见的数据结构,开始接触编程的时候多数程序都和数组相关。刚开始接触Java时也是一直使用数组写一些程序,后来越来越觉得数组这东西没法满足需求了,这时一位“前辈”对我说了一句:不会用集合类就等于没学过Java。然后才知道有集合类。
想想已经是3、4年前的事了,时间如白驹过隙啊。
什么时候数组会显得力不从心,没法满足需求,需要集合类呢?
- 不知道具体数据长度
- 需要自动排序
- 存储键值对
当然,上面的情况不是绝对的,只是数组比较难满足。这时集合类(也可称为容器类)就显示了它强大的功能。
集合类的分类
被水印遮住的单词是Comparator
上图中不包含Queue内容,部分Map的实现类未给出。
常见使用的有List、Set、Map及他们的实现类。
List、Set、Map接口及各实现类的特性
接口 |
特性 |
实现类 |
实现类特性 |
成员要求 |
List |
线性、有序的存储容器,可通过索引访问元素 |
ArrayList |
数组实现。非同步。 |
|
Vector |
类似ArrayList,同步。 |
|
||
LinkedList |
双向链表。非同步。 |
|
||
Map |
保存键值对成员 |
HashMap |
基于哈希表的 Map 接口的实现,满足通用需求 |
任意Object对象,如果修改了equals方法,需同时修改hashCode方法 |
TreeMap |
默认根据自然顺序进行排序,或者根据创建映射时提供的 Comparator进行排序 |
键成员要求实现caparable接口,或者使用Comparator构造TreeMap。键成员一般为同一类型。 |
||
LinkedHashMap |
类似于HashMap,但迭代遍历时取得“键值对”的顺序是其插入顺序或者最近最少使用的次序 |
与HashMap相同 |
||
IdentityHashMap |
使用==取代equals()对“键值”进行比较的散列映射 |
成员通过==判断是否相等 |
||
WeakHashMap |
弱键映射,允许释放映射所指向的对象 |
|
||
ConcurrentHashMap |
线性安全的Map |
|
||
Set |
成员不能重复 |
HashSet |
为快速查找设计的Set |
元素必须定义hashCode() |
TreeSet |
保持次序的Set,底层为树结构 |
元素必须实现Comparable接口 |
||
LinkedHashSet |
内部使用链表维护元素的顺序(插入的次序) |
元素必须定义hashCode() |
在满足要求的情况下,Map应尽量使用HashMap,Set应尽量使用HashSet。
集合类的基本使用
List
List基本操作
ArrayList<String> arrayList = new ArrayList<String>(); arrayList.add("Tom"); arrayList.add("Jerry"); arrayList.add("Micky"); // 使用Iterator遍历元素 Iterator<String> it = arrayList.iterator(); while (it.hasNext()) { String str = it.next(); System.out.println(str); } // 在指定位置插入元素 arrayList.add(2, "Kate"); // 通过索引直接访问元素 for (int i = 0; i < arrayList.size(); i++) { System.out.println(arrayList.get(i)); } List<String> subList = new ArrayList<String>(); subList.add("Mike"); // addAll(Collection<? extends String> c)添加所给集合中的所有元素 arrayList.addAll(subList); // 判断是否包含某个元素 if (arrayList.contains("Mike")) { System.out.println("Mike is include in the list"); } LinkedList<String> linkedList = new LinkedList<String>(); linkedList.addAll(arrayList); // 获取指定元素 System.out.println(linkedList.get(4)); // 获取第一个元素 System.out.println(linkedList.getFirst()); // 获取最后一个元素 System.out.println(linkedList.getLast()); // 获取并删除第一个元素 System.out.println(linkedList.pollFirst()); // 获取,但不移除第一个元素 System.out.println(linkedList.peekFirst());
ArrayList和LinkedList的效率比较
// ArrayList添加元素的效率 ArrayList<String> arrList = new ArrayList<String>(); long startTimeMillis, endTimeMillis; startTimeMillis = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { arrList.add(0, "addString"); } endTimeMillis = System.currentTimeMillis(); System.out.println("ArrayList:" + (endTimeMillis - startTimeMillis) + "ms"); arrList.clear(); startTimeMillis = System.currentTimeMillis(); for (int i = 0; i < 20000; i++) { arrList.add(0, "addString"); } endTimeMillis = System.currentTimeMillis(); System.out.println("ArrayList:" + (endTimeMillis - startTimeMillis) + "ms"); arrList.clear(); startTimeMillis = System.currentTimeMillis(); for (int i = 0; i < 40000; i++) { arrList.add(0, "addString"); } endTimeMillis = System.currentTimeMillis(); System.out.println("ArrayList:" + (endTimeMillis - startTimeMillis) + "ms"); arrList.clear(); startTimeMillis = System.currentTimeMillis(); for (int i = 0; i < 80000; i++) { arrList.add(0, "addString"); } endTimeMillis = System.currentTimeMillis(); System.out.println("ArrayList:" + (endTimeMillis - startTimeMillis) + "ms"); arrList.clear(); startTimeMillis = System.currentTimeMillis(); for (int i = 0; i < 160000; i++) { arrList.add(0, "addString"); } endTimeMillis = System.currentTimeMillis(); System.out.println("ArrayList:" + (endTimeMillis - startTimeMillis) + "ms"); arrList.clear(); startTimeMillis = System.currentTimeMillis(); for (int i = 0; i < 320000; i++) { arrList.add(0, "addString"); } endTimeMillis = System.currentTimeMillis(); System.out.println("ArrayList:" + (endTimeMillis - startTimeMillis) + "ms");
执行时间比较
执行次数(在0号位置插入) |
ArrayList所用时间(ms) |
LinkedList所用时间(ms) |
10000 |
31 |
0 |
20000 |
141 |
0 |
40000 |
484 |
16 |
80000 |
1985 |
0 |
160000 |
7906 |
0 |
320000 |
31719 |
16 |
执行次数(在尾部插入) |
ArrayList所用时间(ms) |
LinkedList所用时间(ms) |
10000 |
0 |
0 |
20000 |
15 |
0 |
40000 |
0 |
0 |
80000 |
0 |
0 |
160000 |
0 |
15 |
320000 |
0 |
16 |
循环输出次数(get(index)方法) |
ArrayList所用时间(ms) |
LinkedList所用时间(ms) |
10000 |
93 |
204 |
20000 |
188 |
797 |
40000 |
328 |
2734 |
80000 |
688 |
13328 |
160000 |
1594 |
62313 |
320000 |
2765 |
太久了…… |
因为ArrayList底层由数组实现,在0号位置插入时将移动list的所有元素,在末尾插入元素时不需要移动。LinkedList是双向链表,在任意位置插入元素所需时间均相同。所以在List中有较多插入和删除操作的情况下应使用LinkedList来提高效率,而有较多索引查询的时候使用 ArrayList(使用增强型的for循环或Iterator遍历LinkedList效率将提高很多)。
Map
Map基本操作
HashMap<String, Integer> map = new HashMap<String, Integer>(); // 向Map中添加元素 map.put("Tom", 26); map.put("Jack", 18); map.put("Micky", 17); map.put("Kate", 15); // 根据Key获取Value System.out.println("Jack is " + map.get("Jack") + " years old"); // 移除 map.remove("Micky"); // 遍历Map for (Entry<String, Integer> entry : map.entrySet()) { System.out.println("name:" + entry.getKey() + " age:" + entry.getValue()); } // Key相同的元素将被覆盖 map.put("Jack", 19); // 根据Key获取Value System.out.println("Jack is " + map.get("Jack") + " years old"); // 判断是否包含某个Key if (map.containsKey("Tom")) { System.out.println(map.get("Tom")); } // 判断是否包含某个Value if (map.containsValue(26)) { System.out.println("The map include the value 26"); } // 判断map是否为空 if (!map.isEmpty()) { // 获取map大小 System.out.println("The map‘s size=" + map.size()); } // 获取Key的集合 for (String str : map.keySet()) { System.out.println(str); } TreeMap<String, Integer> treeMap = new TreeMap<String, Integer>(); treeMap.putAll(map); // 输出内容按照key值排序 for (Entry<String, Integer> entry : treeMap.entrySet()) { System.out.println("name:" + entry.getKey() + " age:" + entry.getValue()); // name:Jack age:19 // name:Kate age:15 // name:Tom age:26 } LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<String, Integer>(); // 向Map中添加元素 linkedHashMap.put("Tom", 26); linkedHashMap.put("Jack", 18); linkedHashMap.put("Micky", 17); linkedHashMap.put("Kate", 15); // 保持了插入的顺序 for (Entry<String, Integer> entry : linkedHashMap.entrySet()) { System.out.println("name:" + entry.getKey() + " age:" + entry.getValue()); // name:Tom age:26 // name:Jack age:18 // name:Micky age:17 // name:Kate age:15 }
Set
Set基础操作
List<Integer> list = new ArrayList<Integer>(); list.add(3); list.add(4); HashSet<Integer> hashSet = new HashSet<Integer>(); hashSet.add(1); hashSet.add(3); hashSet.add(2); hashSet.add(6); // 重复元素将不能被添加 hashSet.add(3); // 只要有元素被添加就返回true if (hashSet.addAll(list)) { System.out.println("Add success"); } // 判断是否存在某个集合 if (hashSet.containsAll(list)) { System.out.println("The hashSet is contain 3 and 4"); } Iterator<Integer> it = hashSet.iterator(); while (it.hasNext()) { System.out.print(it.next() + " "); // 1 2 3 4 6 // 看结果是被排序了,HashSet按照Hash函数排序,Integer值的HashCode就是其int值 } // 换转成数组 Object[] integers = hashSet.toArray(); for (int i = 0; i < integers.length; i++) { System.out.print((Integer) integers[i]); } //移除元素 hashSet.remove(3); TreeSet<String> treeSet = new TreeSet<String>(); treeSet.add("C"); treeSet.add("A"); treeSet.add("D"); treeSet.add("B"); for (Iterator<String> strIt = treeSet.iterator(); strIt.hasNext();) { System.out.print(strIt.next()); // ABCD 按照字母顺序 } LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>(); linkedHashSet.add("C"); linkedHashSet.add("A"); linkedHashSet.add("D"); linkedHashSet.add("B"); for (Iterator<String> linkedIt = linkedHashSet.iterator(); linkedIt .hasNext();) { System.out.print(linkedIt.next()); // CADB 按照插入顺序 }
本文没有对ArrayList及HashMap进行深入的分析,这两个类是集合类中最常用的类,将另开文章进行深入剖析。
ArrayList深入分析:《ArrayList源码分析》