集合在程序设计中有着举足轻重的地位,集合的操作无非就是增删遍历等基本的动作,所以就以上基本操作,本文结合案例及代码分别加以说明。
首先,集合从大的方向来分,分为单列集合(Collection)和双列集合(Map)。简单地说,就是Collection中存储的是单一的内容,而Map集合中存储的是键和值,就像看到的表格,Collection只有一列,所以叫单列集合,而Map有两列,Map的一个元素分为两部分,一列存键,一列存值,并且键唯一不能重复。
下来,先看看Collection集合。
Collection
|---List 元素不唯一,存储和了取出顺序一致。
|---ArrayList 底层数据结构是数组。特点:查询快,增删慢,线程不安全,高效。
|---Vector 底层数据结构是数组。特点:线程安全,效率不如ArrayList,被ArrayList取代。
|---LinkedList 底层数据结构是链接列表。特点:增删快,查询慢。线程不安全,高效。
|---Set 元素唯一,但存储和取出顺序不一致。
|---HashSet 底层数据结构是哈希表。特点:依赖于哈希表。
|---LinkedHashSet 底层数据结构是哈希表和链接列表。特点:元素唯一,有序。
|---TreeSet 底层是二叉树。特点:元素可排序。
整个Collection体系,除最底层的是具体实现类,其他都是接口,只定义一些基本的功能,而具体实现都是在类中。添加删除元素相对简单,这里就一笔带过。而对于遍历而言,Collection提供迭代器遍历,而增强for的遍历其实底层也是靠迭代器实现,暂且认为这是两种方式,对于List集合还可以使用普通for循环配合get()方法进行遍历。
1、针对List集合编写以下测试代码:
package cn.io; import java.util.ArrayList; import java.util.Iterator; public class ListDemo { public static void main(String[] args) { //创建集合对象,同理这里可以用Vector和LinkedList替换ArrayList。 ArrayList<String> arrayList = new ArrayList<>(); //向集合添加元素,共三项。 arrayList.add("Hello"); arrayList.add("World"); arrayList.add("Jason"); //删除添加到集合的Jason,结果中不存在。 arrayList.remove("Jason"); //着重看一下遍历,三种方式完成。 //迭代器遍历 Iterator<String> it = arrayList.iterator(); while (it.hasNext()) { String s = it.next(); System.out.println(s); } //这里再加上使用普通for循环配合迭代器的操作。 for (Iterator<String> iterator = arrayList.iterator(); iterator.hasNext();) { String s = iterator.next(); System.out.println(s); } //遍历方式二,使用增强for对集合进行遍历。 for(String s : arrayList){ System.out.println(s); } //遍历方式三,使用普通for循环配合get()方法遍历。 for (int i = 0; i < arrayList.size(); i++) { String s = arrayList.get(i); System.out.println(s); } //同理,对于Vector和LinkedList的操作都是一样,只是在定义ArrayList的时候换成对应的集合就行。 } }
对于Set集合,JDK没有提供对应的get()方法,所以遍历就不能通过普通for循环配合get()方法来遍历。
2、Set集合的操作示例代码:
Set集合的HashSet操作示例可参考 集合HashSet的使用 中的详细说明。
Set的集合还有另一个就是TreeSet,这个集合的使用请参考 集合TreeSet的使用 中示例。
接下来就是LinkedHashSet,HashSet的子类。这个集合比较特殊,底层结构是链表和哈希表。虽然结构特殊,但是使用起来也没有过分的地方,就把它看成HashSet使用吧,代码示例可参考HashSet,不同的是,它颠覆了Set集合的无序,它的有序靠链表结构保证,唯一靠哈希表结构。
集合Collection总结:List集合有三种遍历方式,Set集合有两种遍历方式。
说完了Collection集合,接下来就是Map集合了。乍一看Map集合的格式就有点怕怕的感觉,其实也蛮简单,先看看Map集合的构架。
Map
|---HashMap 底层数据结构是哈希表。
|---LinkedHashMap 底层数据结构是哈希表和链表。
|---Hashtable 底层数据结构是哈希表。
|---TreeMap 底层数据结构是二叉树。
集合Map的定义格式:Map<K k,V v> map = new HashMap<>();因为Map是接口不能用来创建对象,所以只能使用实现了该接口的类来创建对象,这种写法又是面向对象的第三个特点--多态。
1、看看HashMap集合的代码
package cn.io; import java.util.HashMap; import java.util.Map; import java.util.Set; public class HashMapDemo { public static void main(String[] args) { //创建HashMap集合对象 HashMap<String, String> hashMap = new HashMap<>(); //添加对象到HashMap集合 hashMap.put("柳兰歌", "财务"); hashMap.put("冷文卿", "业务"); hashMap.put("李念儿", "开发"); hashMap.put("柳婵诗", "市场"); hashMap.put("景茵梦", "工程"); //重复添加一条记录 hashMap.put("景茵梦", "设计"); //遍历方式一,先获取键的集合,再根据键找对应的值。 Set<String> set1 = hashMap.keySet(); for(String emp : set1){ System.out.println("职员:" + emp + " 部门:" + hashMap.get(emp)); } //遍历方式二,先获取键值对对象的集合,再根据键值对对象找对应的键和值。 Set<Map.Entry<String, String>> set2 = hashMap.entrySet(); for (Map.Entry<String, String> entry : set2) { System.out.println("职员:" + entry.getKey() + " 部门:" + entry.getValue()); } //从程序结果看到最后一条记录并没有添加到集合,因为Map的键不重复, //所以当第六条记录添加时发现键已存在,就用新值替换掉旧值,最后结果是 “职员:景茵梦 部门:设计” } }
应特别注意:V put(K key, V value),它是将指定的值与此映射中的指定键关联。
也就是说,如果键不存在时,就将键和值存入,如果键已存在,则会用新值替换旧值并将旧值返回。但如果是存储自定义对象,刚需要在类中重写hashCode()和equals()方法才能保证键的唯一。
2、再下来就是TreeMap,TreeMap的唯一和排序需依赖于实现Comparable或Comparator接口。
package cn.io; import java.util.Comparator; import java.util.Set; import java.util.TreeMap; public class TreeMapDemo { public static void main(String[] args) { TreeMap<String, Integer> treeMap = new TreeMap<>(new Comparator<String>() { @Override public int compare(String arg0, String arg1) { int num1 = arg0.length() - arg1.length(); int num2 = num1 == 0 ? arg0.compareTo(arg1) : num1; return num2; } }); treeMap.put("Roxette", 20); treeMap.put("Sarah", 21); treeMap.put("Carpenters", 23); treeMap.put("Britney", 22); treeMap.put("Beyonce", 19); //重复添加一条记录 treeMap.put("Beyonce", 21); Set<String> set1 = treeMap.keySet(); for(String key : set1){ System.out.println("Name:" + key + " Age:" + treeMap.get(key)); } } }
示例中第六条依然会替换原来的值。TreeMap的唯一及排序都是针对键的操作,如果是自定义类需要实现Comparable接口或Comparator接口。虽然String类已经实现了Comparable接口,但是这里实现了Comparator接口,以此为准。
如果是哈希表结构,就需要重写hashCode()和equals()。
如果是二叉树结构,就需实现Comparable或Comparator。