第七章集合Collection
7.1 Iterable 接口
首先是集合Collection的的爸爸,列表List的爷爷 Iterable接口。
实现此接口的类队形可以成为for-each循环的目标,也就是增强for循环,它是java中的一种语法糖。
List<Object> lists = new ArrayList<>();
for(Object obj : lists){
}
jdk1.8之前集合的遍历只有Iteractor这一个方法 ,实现该次接口的方法能够创建一个轻量级的迭代器。用于安全的遍历元素,移除元素,添加元素。
总之就是能创建迭代器进行元素的添加和删除的话,就尽量使用迭代器。
也可以使用迭代器的方式进行遍历。
for (Iterator<Object> it = Collections.emptyIterator();it.hasNext();){
System.out.println(it.next());
}
7.2 Collection 接口
Collection是一个顶级接口,用来定义集合的约定。
List 也是一个顶级接口,它继承了Collection接口,同时也是ArrayList、LinkedList等集合元素的父类。
Set接口位于List接口的同一层次,它也继承了Collection接口。Set接口提供了额外的规定,它对add、equals、hashcode方法提供了额外的标准。
Quene是和Set、List接口并列的Collection的三大接口之一。Quene的设计是用来保持元素的访问顺序。除了Collection接口提供的基础操作外,队列还提供了额外的插入、读取、检查操作。
public static void main(String[] args) {
Collection c = new ArrayList();
Collection c1 = new ArrayList();
c1.add("c1");
c1.add("c111");
//增加
c.add(1);
c.add(2);
c.add("s");
c.add(true);
//把另一个集合中的元素添加进来
c.addAll(c1);
System.out.println(c);
//通过值来移除元素
c.remove("c1");
System.out.println("c = " + c);
//是否有交集
System.out.println(c.contains("c111"));
//是否包含c1集合的全部元素
c.add("c1");
System.out.println(c.containsAll(c1));
//从此集合中删除指定集合中不包含的所有元素。
System.out.println(c.retainAll(c1));
System.out.println("c = " + c);
}
7.2.1 List<> 接口
ArrayList
ArrayList是实现了List接口的可扩容数组(动态数组,负载因子0.5)它的内部是基于数组实现的。
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
-
ArrayList可以实现所有可选择的列表操作,允许所有的元素,包括空值。ArrayList还提供了内部存储
list
的方法,它能够完全替代Vector。 -
只有一个例外,ArrayList不是线程安全的容器。如果多个线程中至少有两个线程修改了ArrayList的结构的话,就会导致线程安全问题。
-
作为替代可以使用线程安全的List —— Collections.synchronizedList。
-
ArrayList有一个容量的概念,这个数组的容量就是List用来存储元素的容量。
List list = Collections.synchronizedList(new ArrayList(...));
-
ArrayList具有快速失败机制fail-fast,能够对ArrayList进行失败检测。当迭代过程中该集合在结构上发生变化时,就有可能会发生fail-fast,即抛出ConcurrentModificationException异常
常用方法
更多详细请去查阅Api文档
public class ArrayListDemo {
public ArrayListDemo() {
}
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("a");
list.add("d");
list.add("c");
list.add("b");
list.add("e");
list.add("f");
list.add(0,"g");
list.remove(1);
list.remove("d");
//是否包含元素
System.out.println(list.contains("f"));
//获得元素的下标
System.out.println(list.indexOf("e"));
//过滤删除
list.removeIf(new Predicate() {
@Override
public boolean test(Object o) {
return o.equals("e");
}
});
//更改下标处的元素
list.set(1,"e");
System.out.println(list.subList(0, 3));
System.out.println(list);
}
}
//removeRange() 只能被继承的子类使用
class ArrayListd extends ArrayList{
public static void main(String[] args) {
ArrayListd list = new ArrayListd();
list.add("a");
list.add("d");
list.add("c");
list.add("b");
list.add("e");
list.removeRange(1,3);
System.out.println(list);
}
}
Vector
Vector同ArrayList 一样,都是基于数组实现的,只不过Vector是一个线程安全的容器,它对内部的每个方法都简单粗暴的上锁,避免多线程引起的安全性问题,但是通常这种同步方式需要的开销比较大,因此,访问元素的效率要远远低于ArrayList。
还有一点在于扩容上,ArrayList扩容后的数组长度会增加50%,而Vector的扩容长度后数组会增加一倍(负载因子为1)
常用方法
更多详细请去查阅Api文档
public class VectorDemo {
public VectorDemo() {
}
public static void main(String[] args) {
/*
和ArrayList不同的是 : Vector是线程安全的数组
*/
Vector v = new Vector();
v.add(1);
v.add(2);
v.add(3);
v.add(1);
v.add(4);
System.out.println(v);
}
}
LinkedList
LinkedList是一个双向链表,允许存储任何元素(包括null值)
-
LinkedList所有的操作都可以表现为双向性的,索引到链表的操作将遍历从头到尾,视哪个距离近为遍历顺序
-
该LinkedList实现链表也不是线程安全的,==如果多线程并发访问链表,并且其中一个线程修改了链表的结构,那么这个链表必须进行外部加锁。==或者使用:
List list = Collections.synchronizedList(new LinkedList());
常用方法
更多详细请去查阅Api文档
public class LinkedListDemo {
public LinkedListDemo() {
}
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("a");
list.add("d");
list.add("c");
list.add("b");
list.add("e");
list.addFirst("k");
list.addLast("k1");
list.remove(2);
list.removeFirst();
list.removeLast();
list.get(3);
list.getFirst();
list.getLast();
}
}
Stack
堆栈是后入先出的容器。它继承了Vector类,提供了通用的push和pop操作,以及栈顶的peek方法,测试栈是否为空的isEmpty方法,和一个寻找与栈顶距离的search方法。
第一次创建栈,不包含任何元素。
一个更完善,可靠性更强的LIFO栈操作由Deque接口和他的实现提供。官方建议应该优先使用这个类。
Deque<Integer> stack = new ArrayDeque<Integer>()
7.2.2 Set<> 接口
HashSet
HashSet是Set接口的实现类,由哈希表支持(实际上HashSet是HashMap(负载因子为0.75)的一个实例)。它不能保证集合的迭代顺序。这个类允许null元素。
- ==该实现类不是线程安全的。如果多线程并发访问Set,并且至少有一个线程修改了set,则必须进行外部加锁。或使用Collections.synchronizedSet()==方法
- 该实现支持fail-fast机制。
常用方法
更多详细请去查阅Api文档
public class HashSetDemo {
public HashSetDemo() {
}
public static void main(String[] args) {
/*
Set 不允许重复元素
HashSet 无序(底层是有一定的规则的)
TreeSet 有序
*/
HashSet<String> stringSet = new HashSet<>();
stringSet.add("a");
stringSet.add("a");
stringSet.add("中暑");
stringSet.add("b");
stringSet.add("田");
/*
调用hash()方法, 效率高 但是可能会有重复的hash值,所以不安全
再使用equals() 安全性高
*/
System.out.println("stringSet = " + stringSet);
HashSet<Student> studentSet = new HashSet<>();
studentSet.add(new Student(101,"jim"));
studentSet.add(new Student(102,"jim1"));
studentSet.add(new Student(103,"jim2"));
studentSet.add(new Student(104,"jim3"));
studentSet.add(new Student(101,"jim"));
System.out.println(studentSet);
}
}
TreeSet
TreeSet是基于TreeMap的NavigableSet实现的。==这些元素使用他们的自然排序或者在创建时提供的Comparactor进行排序,==具体取决于使用的构造函数
-
此实现为基本操作add、remove和contains提供了log(n)的时间成本。
-
该实现也不是线程安全的,==如果有多个线程并发访问,并且至少有一个线程修改了TreeSet,则必须进行外部加锁。==或使用:
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...))
-
该实现也支持fail-fast机制
常用方法
更多详细请去查阅Api文档
public class TreeSetDemo {
public TreeSetDemo() {
}
public static void main(String[] args) {
TreeSet<String> strTree = new TreeSet<>();
strTree.add("e");
strTree.add("a");
strTree.add("c");
strTree.add("b");
strTree.add("f");
strTree.add("d");
strTree.add("a");
System.out.println("strTree = " + strTree);
/*
TreeSet 元素不可重复
有序 指定类必须要实现 Comparable中的compareTo方法
*/
TreeSet<Student> treeStudent = new TreeSet<>();
treeStudent.add(new Student(101,"jim"));
treeStudent.add(new Student(102,"jim1"));
treeStudent.add(new Student(104,"jim3"));
treeStudent.add(new Student(105,"jim2"));
treeStudent.add(new Student(101,"jim"));
System.out.println(treeStudent);
}
}
//Compartor 比较器
//Comparable 实现该接口才有排序能力
public class Student implements Comparable<Student>{
int num;
String name;
public Student() {
}
public Student(int num, String name) {
this.num = num;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return num == student.num &&
name.equals(student.name);
}
@Override
public int hashCode() {
return Objects.hash(num, name);
}
@Override
public int compareTo(@NotNull Student o) {
//return this.num - o.num;
return this.name.compareTo(o.name);
}
@Override
public String toString() {
return "Student{" +
"num=" + num +
", name='" + name + '\'' +
'}';
}
}
LinkedHashSet
LinkedHashSet是Set接口的Hash表和LinkedList的实现,该实现不同于HashSet的是它是一个贯穿所有元素的双向链表。此链表定义了元素插入集合的顺序。
注意:如果元素重新插入,则插入的顺序不受影响。
- LinkedHashSet有两个影响其构成的参数︰初始容量和加载因子。它们的定义与HashSet 完全相同。但请注意: 对于LinkedHashSet,选择过高的初始容量值的开销要比 HashSet小,因为LinkedHashSet的迭代次数不受容量影响。
- LinkedHashSet也不是线程安全的,如果多线程并发访问LinkedHashSet,必须加锁,或使用Collections.synchronizedSet 。
- 该实现也支持fail-fast机制
7.3 Map<> 接口
7.3.1 HashMap
==HashMap是一个利用Hash表原理来存储元素的集合。==并且允许空的键值对。HashMap是非线程安全的,也就是说在多线程环境下可能会出现一些问题。而Hashtable是线程安全的容器。
HashMap 的实例有两个参数影响其性能︰初始容量和加载因子。可以使用Collections.synchronizedMap(new HashMap(…))来构造一个线程安全的 HashMap。
- 该实现也支持fail-fast机制
遍历方法
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for(Map.Entry entry : entries){
System.out.println(entry.getKey()+"="+entry.getValue());
}
7.3.2 TreeMap
==一个基于NavigableMap实现的红黑树。==这个map是根据自然排序存储,或者通过Compartor进行定制排序。
-
TreeMap为containsKey、get、put和remove方法提供了log(n)的时间开销。
-
注意这个实现不是线程安全的。如果多线程并发访问TreeMap,并且至少一个线程修改了map,必须进行外部加锁。这通常通过在自然封装集合的某个对象上进行同步来实现,或者使用:
SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...))
-
该实现也支持fail-fast机制
7.3.3 LinkedHashMap
==LinkedHashMap是Map 接口的哈希表和链表的实现。这个实现与HashMap 不同之处在于它维护了一个贯穿其所有条目的双向链表。==这个链表定义了遍历顺序,通常是插入map 中的顺序。
-
它提供一个特殊的LinkedHashMap(int,float, boolean)构造器来创建LinkedHashMap,其遍历顺序是其最后一次访问的顺序。
-
可以重写 removeEldestEntry(Map.Entry)方法,以便在将新映射添加到map时强制删除过期映射的策略。
-
这个类提供了所有可选择的map操作,并且允许null元素。由于维护链表的额外开销,性能可能会低于HashMap,有一条除外︰遍历LinkedHashMap 中的collection-views 需要与map.size成正比,无论其容量如何。HashMap的迭代看起来开销更大,因为还要求时间与其容量成正比。
-
LinkedHashMap有两个因素影响了它的构成∶初始容量和加载因子。
-
注意这个实现不是线程安全的。如果多线程并发访问LinkedHashMap,并且至少一个线程修改了map,必须进行外部加锁。这通常通过在自然封装集合的某个对象上进行同步来实现,或使用:
Map m =Collections.synchronizedMap(new LinkedHashMap( . .. ))
-
这个实现持有fail-fast机制。
7.3.4 Hashtable
Hashtable类实现了一个哈希表,能够将键映射到值。任何非空对象都可以用作键或值。
- 此实现类支持fail-fast 机制
- 与新的集合实现不同,Hashtable是线程安全的。如果不需要线程安全的容器,推荐使用HashMap,如果需要多线程高并发,推荐使用ConcurrentHashMap 。
7.3.5 WeakHashMap
WeakHashMap类基于哈希表的Map基础实现,带有弱键。WeakHashMap 中的entry当不再使用时还会自动移除。更准确的说,给定key的映射的存在将不会阻止 key被垃圾收集器丢弃。
- 基于map 接口,是一种弱键相连, WeakHashMap 里面的键会自动回收
- 支持null值和null键。
- fast-fail 机制
- 不允许重复
- WeakHashMap 经常用作缓存
7.4 Collections 工具类