List接口和Set接口
Collection下面有Set(无序集合,元素不可重复),Queue(队列),List(有序集合,元素可以集合)。
Set :EnumSet,SortedSet(TreeSet), HashSet(LinkedHashSet)
Queue : Deque(ArrayDeque, LinkedList), Priority
List :ArrayList, Vector(Stack)
Set接口
HashSet类
HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
特点:
- 不能保持元素的排列顺序,顺序可以与添加顺序不同,顺序也有可能发生变化。
- HashSet不是同步的, 如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合,则必须通过代码来保证其同步。
- 集合元素值可以是null
public class TestInteger {
public static void main(String[] args) {
//唯一,无序,底层哈希表实现
HashSet<Integer> hs = new HashSet<>();
hs.add(19);
hs.add(5);
hs.add(20);
hs.add(19);
hs.add(41);
hs.add(0);
System.out.println(hs.size());
System.out.println(hs);
}
}
LinkedHashSet类
LinekedHashSet说HashSet的子类,LinkedHashSet需要维护元素的插入顺序,因此性能略低于HashSet性能,但在迭代访问Set里的全部元素时将有很好的性能。
底层也是哈希表+链表来实现的
特点: 唯一,有序(按照输入顺序进行输出)
public class LinkedHashSetTest {
public static void main(String[] args) {
LinkedHashSet books = new LinkedHashSet();
books.add("疯狂java讲义");
books.add("轻量级java EE企业应用实战");
books.add("疯狂java讲义");
System.out.println(books);
books.remove("疯狂java讲义");
books.add("疯狂java讲义");
System.out.println(books);
}
}
TreeSet类
TreeSet是SortSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
特点: 唯一,无序(没有按照输入顺序,进行输出), 有序(按照升序进行遍历)
底层是红黑树
public class TreeSetTest {
public static void main(String[] args) {
TreeSet nums = new TreeSet();
nums.add(5);
nums.add(2);
nums.add(10);
nums.add(-9);
System.out.println(nums);
System.out.println(nums.first());
System.out.println(nums.last());
System.out.println(nums.headSet(4));
System.out.println(nums.tailSet(5));
System.out.println(nums.subSet(-3,4));
}
}
自然排序
TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列,这种方式就是自然排列。
class R implements Comparable{
int count;
public R(int count){
this.count = count;
}
public String toString(){
return "R[count: "+count+"]";
}
public boolean equals(Object obj){
if (this == obj){
return true;
}
if (obj != null && obj.getClass() == R.class){
R r = (R) obj;
return r.count == this.count;
}
return false;
}
@Override
public int compareTo(Object obj) {
R r = (R)obj;
return count > r.count?1:count <r.count ? -1:0;
}
}
public class TreeSetTest3 {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new R(5));
ts.add(new R(-3));
ts.add(new R(9));
ts.add(new R(-2));
System.out.println(ts);
R first = (R)ts.first();
first.count = 20;
System.out.println(ts);
R last = (R)ts.last();
last.count = -2;
System.out.println(ts);
}
}
定制排序
TreeSet的自然排序是根据集合元素的大小,TreeSet将它们以升序排列。如果需要实现定制排序,则可以通过Comparator接口的帮助。
class M{
int age;
public M(int age){
this.age = age;
}
public String toString(){
return "M [age:"+age+"]";
}
}
public class TreeSetTest4 {
public static void main(String[] args) {
TreeSet ts = new TreeSet((o1, o2) -> {
M m1 = (M) o1;
M m2 = (M) o2;
return m1.age > m2.age ?-1:
m1.age < m2.age ? 1:0;
});
ts.add(new M(5));
ts.add(new M(-3));
ts.add(new M(9));
System.out.println(ts);
}
}
EnumSet类
EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显示或隐式地指定。EnumSet地集合元素也是有序的。EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。
enum Season{
SPRING, SUMMER,FALL,WINNER
}
public class EnumSetTest {
public static void main(String[] args) {
EnumSet se1 = EnumSet.allOf(Season.class);
System.out.println(se1);
EnumSet se2 = EnumSet.noneOf(Season.class);
System.out.println(se2);
}
}
各Set实现类的性能分析
HashSet和TreeSet是Set的两个典型实现,到底如何选择呢?
HashSet的性能总是比TreeSet好(特别是最常用的添加,查询元素等操作)
因为TreeSet需要额外的红黑树算法来维护集合元素的次序。
只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet
HashSet还有一个子类:
LinkedHashSet,对于普通的插入,删除操作,LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashSet会更快。
EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。
Set的三大实现类HashSet, TreeSet, EnumSet都是线程不安全的。
如果有多个线程同时访问一个Set集合,并且有超过一个线程修改了该Set集合,则必须手动保证该Set集合的同步性。
list接口
List接口和ListIterator
List作为Collection 接口的子接口,当然可以使用Collection接口里的全部方法。
List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。
public class TestList {
public static void main(String[] args) {
// 有序,不唯一
List list = new ArrayList();
list.add(13);
list.add(17);
list.add(6);
list.add(-1);
list.add("abc");
System.out.println(list);
list.add(3,66);
System.out.println(list);
list.set(3,77);
System.out.println(list);
list.remove(2);
System.out.println(list);
list.remove("abc");
System.out.println(list);
Object o = list.get(0);
System.out.println(o);
// list集合 遍历
// 方式: 普通for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
for (Object o1 : list) {
System.out.println(o1);
}
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
}
与Set只提供了一个iterator()方法不同,List还额外提供了一个ListIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口
public class Test01 {
public static void main(String[] args) {
// ArrayList<String> list = new ArrayList<>();
ArrayList list = new ArrayList();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
String[] strings = new String[10];
// 错误
// list与it对集合同时进行操作
// Iterator<String> it = list.iterator();
// while (it.hasNext()){
// if ("cc".equals(it.next())){
// list.add("kk");
//
// }
// }
ListIterator<String> it = list.listIterator();
while (it.hasNext()){
if ("cc".equals(it.next())){
it.add("kk");
}
}
// System.out.println(it.hasNext());
// System.out.println(it.hasPrevious());
逆向遍历
// System.out.println(it.hasNext());
// System.out.println(it.hasPrevious());
while (it.hasPrevious()){
System.out.println(it.previous());
}
}
}
ArrayList和Vector实现类
数组优点: 查询效率高
特点: 数组可以重复
数组缺点: 删除,增加元素效率低
遍历方式相同
方式一: 普通的for循环
方式二: 增强for循环
方式三: 迭代器(Iterator, ListIterator)
ArrayList实现类: 类似于StringBuilder
JDK1.7源码:
底层数组,在调用构造器的时候,数组长度初始化10, 扩容的时候扩展为原数组的1.5倍
JDK1.8源码:
底层数组,在调用构造器的时候,底层数组为{}, 在调用add方法以后底层数组才重新赋值为新数组,新数组的长度为10-》 节省内存,在add后才创建长度为10的数组
Vector实现类
联系: 底层数组都是数组的扩容
区别:
ArrayList底层扩容长度为原数组的1.5倍 线程不安全,效率高
Vector底层扩容长度为原数组的2倍 线程安全 效率低 (淘汰)
Queue集合
Queue用于模拟队列这种数据结构,队列通常是指 “先进先出” 的容器。队列的头部保存在队列中存放时间最长的元素,队列的尾部保存在队列存放时间最短的元素。新元素插入到队列的尾部,访问元素操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素。
PriorityQueue 实现类
PriorityQueue 是一个比较准确的队列实现类。之所以说它是比较标准的队列实现,而不是绝对标准的队列实现。,是因为 PriorityQueue 保存队列元素的顺序并不是按加入队列顺序,而是按队列元素的大小进行重新排序。
public class PriorityQueueTest {
public static void main(String[] args) {
PriorityQueue pq = new PriorityQueue();
pq.offer(6);
pq.offer(-3);
pq.offer(20);
pq.offer(18);
System.out.println(pq);
System.out.println(pq.poll());
}
}
Deque 接口与ArrayDeque 实现类
Deque接口是 Queue接口的子接口,它代表一个双端队列,Deque接口里定义了一些双端队列的方法。也可以为栈来使用。
把ArrayDeque 当成 “栈”
public class ArrayDequeStack {
public static void main(String[] args) {
ArrayDeque stack = new ArrayDeque();
stack.push("疯狂Java讲义");
stack.push("轻量级Java EE企业级应用实战");
stack.push("疯狂Android");
System.out.println(stack);
System.out.println(stack.peek());
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack);
}
}
作为队列使用
public class ArrayDequeQueue {
public static void main(String[] args) {
ArrayDeque queue = new ArrayDeque();
queue.offer("疯狂Java讲义");
queue.offer("轻量级Java EE企业应用实战");
queue.offer("疯狂Android讲义");
System.out.println(queue);
System.out.println(queue.peek());
System.out.println(queue);
System.out.println(queue.poll());
System.out.println(queue);
}
}
LinkedList实现类
LinkedList类是List接口的实现类---- 这就意味它是一个List集合,可以根据索引来随机访问集合中的元素。除此之外,LinkedList还实现了Deque接口,可以被当成双端队列使用,因此即可以被当成"栈"来使用,也可以当成队列使用。
public class LinkedListTest {
public static void main(String[] args) {
LinkedList books = new LinkedList();
books.offer("疯狂Java讲义");
books.push("轻量级Java EE企业应用实战");
books.offerFirst("疯狂Android讲义");
for (int i = 0; i < books.size(); i++) {
System.out.println("遍历中: "+books.get(i));
}
System.out.println(books.peekFirst());
System.out.println(books.peekLast());
System.out.println(books.pop());
System.out.println(books);
System.out.println(books.pollLast());
System.out.println(books);
}
}
各List实现类的性能分析
java提供的List就是一个线性表接口,而ArrayList, LinkedList又是线性表的两种典型实现: 基于数组的线性表和基于链表的线性表。Queue代表了队列,Deque代表了双端队列。(即可作为队列使用,也可作为栈使用)。
- 如果需要遍历List集合元素, 对于ArrayList, Vector 集合,使用随机访问方法来遍历集合元素,这样性能更好: 对于LinkedList集合,则应该采用迭代器来遍历集合元素。
- 如果需要经常执行插入,删除操作来改变包含大量数据的 List集合的大小,可考虑使用LinkedList集合。使用ArrayList, Vector集合可能需要经常重新分配内部数组的大小,效果可能较差。
- 如果有多个线程需要同时访问List集合种的元素,开发者可考虑使用使用Collections将集合包装成线程安全集合。