什么是集合?通俗的讲,集合就是存储一组数据的容器,那么,相比较于同样是存储数据的数组,集合的优势就体现在集合的长度是可变的,而数组的长度是固定的。在我们常见的集合中,主要有两大类:
Collection接口单列集合和Map双列集合(键值对集合)接口,其中实现Collection接口的集合主要有:List、Set、Queue(队列),实现Map接口的集合主要是HashMap、LinkedHashMap、TreeMap等。下面分别介绍这些集合是如何来便利的。
List集合(接口)
特点:有序,可重复
主要实现类:ArrayList:内部基于数组,查找元素、遍历集合的速度快
Linkedlist:内部基于链表,插入、删除元素的速度快
以ArrayList为例,遍历集合
ArrayList<String> list = new ArrayList<String>();list.add("小鲁班");list.add("貂蝉");list.add("后羿");list.add("白起");list.add("亚瑟");list.add("百里守约"); // 方式1:foreach循环for (String hero : list) {System.out.print(hero + " ");}System.out.println();// 方式2:for循环for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i) + " ");}System.out.println();// 方式3:迭代器Iterator<String> it = list.iterator();while (it.hasNext()) {String hero = it.next();System.out.print(hero + " ");}// 运行结果相同// 小鲁班 貂蝉 后羿 白起 亚瑟 百里守约
Set集合(接口)
特点:无序,元素不可重复,只用使用迭代器和foreach循环遍历
主要实现类:HashSet:内部基于HashMap,无序,元素不可重复
HashSet<String> set = new HashSet<String>();set.add("小鲁班");set.add("貂蝉");set.add("后羿");set.add("白起");set.add("亚瑟");set.add("百里守约");// foreach遍历for(String s : set) {System.out.print(s + " ");}// 运行结果// 白起 貂蝉 小鲁班 百里守约 后羿 亚瑟
LinkedHashSet:内部基于LinkedHashMap,有序(根据添加的顺序),元素不可重复
LinkedHashSet<String> set = new LinkedHashSet<String>();set.add("小鲁班");set.add("貂蝉");set.add("后羿");set.add("白起");set.add("亚瑟");set.add("百里守约");// foreach遍历for(String s : set) {System.out.print(s + " ");}// 运行结果// 小鲁班 貂蝉 后羿 白起 亚瑟 百里守约
TreeSet:内部基于TreeMap,可以自动排序(实现Comparable接口和Comparator接口,需要重写compareTo方法,实现排序逻辑,如果添加的元素没有实现Comparable接口,那么需要在构造方法时,传入一个Comparator实现类对象),元素不可重复
TreeSet<String> set = new TreeSet<String>();set.add("小鲁班");set.add("貂蝉");set.add("后羿");set.add("白起");set.add("亚瑟");set.add("百里守约");// 迭代器Iterator<String> it = set.iterator();while (it.hasNext()) {String name = it.next();System.out.print(name + " ");}// 运行结果// 亚瑟 后羿 小鲁班 白起 百里守约 貂蝉
Queue队列(接口)
特点:线性表结构,遵循先进先出(FIFO)原则,一般来说,只允许在集合前端进行出队操作,在集合后端进行入队操作
遍历方式:foreach循环遍历、迭代器遍历、while循环判空遍历
有界队列:队列容量受构造方法的容量参数限制,主要实现类:ArrayBlockingQueue,超出容量限制时,会抛出IllegalStateException异常
无界队列:队列容量没有限制,主要实现类:LinkedList
优先级队列:添加的元素必须实现Comporable接口,如果没有实现,必须在构造方法时传入一个Comparator实现类对象,内部通过用数组表示的小顶堆实现,队首永远是优先级最高(根据重写的comparTo方法实现的逻辑排序)的元素
以优先级队列为例
Queue<String> queue = new PriorityQueue<String>();queue.offer("C");queue.offer("B");queue.offer("D");queue.offer("A");queue.offer("E");// foreach遍历for(String s : queue) {System.out.print(s+" ");}System.out.println();// 迭代器遍历Iterator<String> it = queue.iterator();while (it.hasNext()) {String s = it.next();System.out.print(s + " ");}System.out.println();// 运行结果相同// A B D C E // 使用isEmpty()方法对队列进行判空,然后出队遍历while(!queue.isEmpty()) {System.out.print(queue.poll() + " ");}// 由于优先级不同,运行结果与上面不同// A B C D E
Deque双端队列(接口)
特点:线性表结构,相比于Queue可在集合两端进行出队、入队操作
遍历方式:foreach循环遍历、迭代器遍历、while循环判空遍历
Deque<String> deque = new LinkedList<String>();deque.offer("A"); // 队尾deque.offerFirst("V"); // 队头deque.offer("S"); // 队尾deque.offerLast("G");deque.offer("E");System.out.println(deque);// 运行结果// [V, A, S, G, E]// 迭代器Iterator<String> it = deque.iterator();while (it.hasNext()) {String s = it.next();System.out.print(s + " ");}// foreach循环for (String s : deque) {System.out.print(s + " ");}// 运行结果// V A S G E
注意:以下两种方法只能单独使用,当遍历时,由于使用了poll()方法,队列中的元素已经出队,当出队完毕时,队列中已经没有元素,需要再次入队才能进行下一次操作。
// 从队首开始遍历并出队String item = null;while((item = deque.pollFirst())!= null) {System.out.print(item + " ");}// 运行结果// V A S G E
// 从队尾开始遍历并出队String item = null;while((item = deque.pollLast())!= null) {System.out.print(item + " ");}// 运行结果// E G S A V
Stack栈(类)
特点:后进先出(LIFO),只有入栈和出栈操作
遍历方式:foreach循环遍历、迭代器遍历、while循环判空遍历
Stack继承自Vector类,而Vector实现了List接口,由于Vector的方法都是同步的(Synchronized),是线程安全的,而与之类似的ArrayList的方法不是同步的,因此,ArrayList的性能会比Vector好。
Stack<String> stack = new Stack<String>();stack.push("A1");stack.push("A2");stack.push("A3");stack.push("A4");stack.push("A5");// foreachfor(String s : stack) {System.out.print(s + " ");}System.out.println();// 迭代器Iterator<String> it = stack.iterator();while (it.hasNext()) {String s = it.next();System.out.print(s + " ");}System.out.println();// 遍历并出栈while(!stack.isEmpty()) {System.out.print(stack.pop() + " ");}// 运行结果// A1 A2 A3 A4 A5 // A1 A2 A3 A4 A5 // A5 A4 A3 A2 A1
由于栈是后进先出原则,在pop时,会先弹出栈顶元素,也就是最后入栈的元素,因此运行结果与前两种不同。
Map集合
特点:键值对集合,映射表,键唯一,值不唯一,一个键可以对应多个值,可以通过键快速查找值
遍历方式:foreach循环遍历KeySet()、foreach循环遍历entrySet()
主要实现类:HashMap:数据结构采用数组+链表+红黑树,数组类型为Node[],每个Node都保存了某个KV键值对的key、value、hash、next等值,当添加新元素时,会按照key的hash值与它的高16位进行异或运算((n-1)^hash),计算数组中的存储位置下标,如果该下标位置已经存在元素,代表哈希冲突,则采用链地址法存储到链表尾部,当链表Node节点数量大于8,如果当数组长度大于64时,会将链表转换成一个红黑树。当数组长度小于64时,就会进行数组扩容,扩容容量是当前容量的2倍;同时如果hashMap中的元素个数达到扩容阈值(数组容量(默认为16)*加载因子(默认为0.75))时,也会进行扩容。
HashMap<String, Integer> map = new HashMap<String, Integer>();map.put("apple", 5);map.put("banana", 9);map.put("orange", 15);map.put("pear", 7);// foreach循环遍历KeySet()for (String key : map.keySet()) {Integer value = map.get(key);System.out.print(key + ":" + value + " ");}System.out.println();// foreach循环遍历entrySet()for (Entry<String, Integer> e : map.entrySet()) {String key = e.getKey();Integer value = e.getValue();System.out.print(key + ":" + value + " ");}// 运行结果相同// banana:9 orange:15 apple:5 pear:7
LinkedHashMap:HashMap的子类,有序,遍历时顺序与添加顺序相同
LinkedHashMap<String, Integer> map = new LinkedHashMap<String, Integer>();map.put("apple", 5);map.put("banana", 9);map.put("orange", 15);map.put("pear", 7);// foreach循环遍历KeySet()for (String key : map.keySet()) {Integer value = map.get(key);System.out.print(key + ":" + value + " ");}System.out.println();// foreach循环遍历entrySet()for (Entry<String, Integer> e : map.entrySet()) {String key = e.getKey();Integer value = e.getValue();System.out.print(key + ":" + value + " ");}// 运行结果相同// apple:5 banana:9 orange:15 pear:7
TreeMap:实现了SortedMap接口,可以自动按照排序规则排序,key必须实现Comparable接口或Comparator接口,需要重写compareTo方法,实现排序逻辑,如果添加的元素没有实现Comparable接口,那么需要在构造方法时,传入一个Comparator实现类对象
import parator;import java.util.Map;import java.util.TreeMap;import java.util.Map.Entry;public class Test {public static void main(String[] args) {Map<Person, Integer> map = new TreeMap<Person, Integer>(new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return pareTo(o2.name);}});map.put(new Person("Tom"), 90);map.put(new Person("Bob"), 76);map.put(new Person("Lily"), 59);// foreach循环遍历KeySet()for (Person key : map.keySet()) {Integer value = map.get(key);System.out.print(key + ":" + value + " ");}System.out.println();// foreach循环遍历entrySet()for (Entry<Person, Integer> e : map.entrySet()) {Person key = e.getKey();Integer value = e.getValue();System.out.print(key + ":" + value + " ");}// 运行结果相同// Person [name=Bob]:76 Person [name=Lily]:59 Person [name=Tom]:90 }}class Person {public String name;Person(String name) {this.name = name;}@Overridepublic String toString() {return "Person [name=" + name + "]";}}