什么是集合?通俗的讲,集合就是存储一组数据的容器,那么,相比较于同样是存储数据的数组,集合的优势就体现在集合的长度是可变的,而数组的长度是固定的。在我们常见的集合中,主要有两大类:
Collection接口单列集合和Map双列集合(键值对集合)接口,其中实现Collection接口的集合主要有:List、Set、Queue(队列),实现Map接口的集合主要是HashMap、LinkedHashMap、TreeMap等。下面分别介绍这些集合是如何来便利的。
List集合(接口)特点:有序,可重复
主要实现类:ArrayList:内部基于数组,查找元素、遍历集合的速度快
Linkedlist:内部基于链表,插入、删除元素的速度快
以ArrayList为例,遍历集合
ArrayListSet集合(接口)list = new ArrayList (); 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 it = list.iterator(); while (it.hasNext()) { String hero = it.next(); System.out.print(hero + " "); } // 运行结果相同 // 小鲁班 貂蝉 后羿 白起 亚瑟 百里守约
特点:无序,元素不可重复,只用使用迭代器和foreach循环遍历
主要实现类:HashSet:内部基于HashMap,无序,元素不可重复
HashSetset = new HashSet (); set.add("小鲁班"); set.add("貂蝉"); set.add("后羿"); set.add("白起"); set.add("亚瑟"); set.add("百里守约"); // foreach遍历 for(String s : set) { System.out.print(s + " "); } // 运行结果 // 白起 貂蝉 小鲁班 百里守约 后羿 亚瑟
LinkedHashSet:内部基于LinkedHashMap,有序(根据添加的顺序),元素不可重复
LinkedHashSetset = new LinkedHashSet (); 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实现类对象),元素不可重复
TreeSetQueue队列(接口)set = new TreeSet (); set.add("小鲁班"); set.add("貂蝉"); set.add("后羿"); set.add("白起"); set.add("亚瑟"); set.add("百里守约"); // 迭代器 Iterator it = set.iterator(); while (it.hasNext()) { String name = it.next(); System.out.print(name + " "); } // 运行结果 // 亚瑟 后羿 小鲁班 白起 百里守约 貂蝉
特点:线性表结构,遵循先进先出(FIFO)原则,一般来说,只允许在集合前端进行出队操作,在集合后端进行入队操作
遍历方式:foreach循环遍历、迭代器遍历、while循环判空遍历
有界队列:队列容量受构造方法的容量参数限制,主要实现类:ArrayBlockingQueue,超出容量限制时,会抛出IllegalStateException异常
无界队列:队列容量没有限制,主要实现类:LinkedList
优先级队列:添加的元素必须实现Comporable接口,如果没有实现,必须在构造方法时传入一个Comparator实现类对象,内部通过用数组表示的小顶堆实现,队首永远是优先级最高(根据重写的comparTo方法实现的逻辑排序)的元素
以优先级队列为例
QueueDeque双端队列(接口)queue = new PriorityQueue (); 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 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
特点:线性表结构,相比于Queue可在集合两端进行出队、入队操作
遍历方式:foreach循环遍历、迭代器遍历、while循环判空遍历
| Queue | Deque | |
| 添加元素到队尾 | add(E e) / offer(E e) | addLast(E e) / offerLast(E e) |
| 取首元素并删除 | E remove() / E poll() | E removeFirst() / E pollFirst() |
| 取首元素并不删除 | E element() / E peek() | E getFirst / E peekFirst() |
| 添加元素到队首 | 无 | addFirst(E e) / offerFirst(E e) |
| 取队尾元素并删除 | 无 | E removeLast() / E pollLast() |
| 取队尾元素但不删除 | 无 | E getLast / E peekLast() |
Dequedeque = new LinkedList (); 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 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好。
| 压栈 | push(E e) |
| 把栈顶元素弹出 | E pop() |
| 取出栈顶元素但不弹出 | E peek() |
Stackstack = new Stack (); stack.push("A1"); stack.push("A2"); stack.push("A3"); stack.push("A4"); stack.push("A5"); // foreach for(String s : stack) { System.out.print(s + " "); } System.out.println(); // 迭代器 Iterator 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))时,也会进行扩容。
HashMapmap = new HashMap (); 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 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的子类,有序,遍历时顺序与添加顺序相同
LinkedHashMapmap = new LinkedHashMap (); 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 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 java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;
public class Test {
public static void main(String[] args) {
Map map = new TreeMap(new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o1.name.compareTo(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 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;
}
@Override
public String toString() {
return "Person [name=" + name + "]";
}
}



