一、Collection接口
1.1 Collection接口概述
Collection接口是所有后续集合类型的一个公共抽象定义。它本身没有一个直接的实现,更多的是各种不同的集合类型在它的基础上继承了更多特殊的特性并做了一个实现。
Collection接口是Set、List和Queue接口的父接口,并且定义了一个包含一批对象的集合。
接口的主要方法包括:
-
size():集合内的对象数量
-
add(E)/addAll(Collection):向集合内添加单个/批量对象
-
remove(Object)/removeAll(Collection):从集合内删除单个/批量对象
-
contains(Object)/containsAll(Collection):判断集合中是否存在某个/某些对象
-
toArray():返回包含集合内所有对象的数组
-
iterator():返回Iterator对象,用于遍历集合中的元素
二、List接口
2.1 List接口概述
List接口继承自Collection,用于定义以列表形式存储的集合,List接口为集合中的每个对象分配了一个索引(index),标记该对象在List中的位置,并可以通过index定位到指定位置的对象。
(1)List类型集合特点:
集合中的元素有序且可重复,有下标。
注:有序指的是元素放到集合中的顺序和循环遍历出来的顺序一致。
(2)List接口常见的实现类有:ArrayList、LinkedList、Vector等。
(3)各个实现类区别:
对于数据的随机访问,ArrayList效率优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。Vector是线程安全的集合,但是速度慢
(4)List在Collection基础上增加的主要方法包括:
-
get(int) - 返回指定index位置上的对象
-
add(E)/add(int, E) - 在List末尾/指定index位置上插入一个对象
-
set(int, E) - 替换置于List指定index位置上的对象
-
indexOf(Object) - 返回指定对象在List中的index位置
-
subList(int,int) - 返回指定起始index到终止index的子List对象
-
ListIterator - Iterator的子接口,List特有迭代器
*为什么要有这个特定的Iterator子接口:
在迭代时,不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。
所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,
如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。该接口只能通过List集合的listIterator方法获取。
示例:
在一个集合中判断里面有没有“hello”这个元素,如果有,就添加一个”word”元素。 代码如下:
Iterator<String> it = list.iterator();
while (it.hasNext()){
String str = (String)it.next();
if(str.equals("我")){
list.add("是"); //这里会抛出ConcurrentModificationException并发修改异常
}
}
如果用ListIterator就能避免错误。
代码如下:
ListIterator lit = list.listIterator();
//如果想在遍历的过程中添加元素,用ListIterator中的add方法 while**(lit.hasNext()) {
String str = (String)lit.next();
if(str.equals("hello")) {
lit.add("world");
}
}
2.2 ArrayList
ArrayList基于数组来实现集合的功能,其内部维护了一个可变长的对象数组,集合内所有对象存储于这个数组中,并实现该数组长度的动态伸缩,在查询数据上面性能好。
(1)用contains 去除ArrayList中重复的元素:
public static ArrayList getSingle(ArrayList list) {
ArrayList newList = new ArrayList();
Iterator it = list.iterator(); //获取迭代器
while(it.hasNext()) { //判断老集合中是否有元素
String temp = (String)it.next();
if(!newList.contains(temp)) {
newList.add(temp); //将该元素添加到新集合中
}
}
return newList; //将新集合返回
}
2.3 LinkedList
LinkedList基于链表来实现集合的功能,其实现了静态类Node,集合中的每个对象都由一个Node保存,每个Node都拥有到自己的前一个和后一个Node的引用。
它实现了List接口和Deque接口,说明它具有两边接口的特性,因此它可以当作一个双端队列来用,也可以当作栈来用,并且它是以链表的形式来实现的,所以查询性能差,但是增加和删除操作性能高。
(1)LinkedList特有的方法
** public void addFirst(E e)及addLast(E e)*
** public E getFirst()及getLast()*
** public E removeFirst()及public E removeLast()*
** public E get(int index);*
(2)用LinkedList模拟栈数据结构
public class Stack {
private LinkedList list = new LinkedList();
public void in(Object obj) {
list.addLast(obj); //封装addLast()方法
}
public Object out() {
return list.removeLast(); //封装removeLast()方法
}
public boolean isEmpty() {
return list.isEmpty(); //封装isEmpty()方法
}
}
2.4 Vector
Vector和ArrayList很像,都是基于数组实现的集合。
(1)它和ArrayList的主要区别在于:
Vector是线程安全的,而ArrayList不是
由于Vector中的方法基本都是synchronized的,其性能低于ArrayList
Vector可以定义数组长度扩容的因子,ArrayList不能
(2)特有方法
public void addElement(E obj)
public E elementAt(int index)
public Enumeration elements()
(3)使用枚举遍历
Vector v = new Vector(); //创建集合对象,List的子类
v.addElement("a");
v.addElement("b");
v.addElement("c");
v.addElement("d");
//Vector迭代
Enumeration en = v.elements(); //获取枚举
while(en.hasMoreElements()) { //判断集合中是否有元素
System.out.println(en.nextElement());//获取集合中的元素
}
2.5 Stack
用来模拟“栈”这种数据结构。
2.6 总结
List接口三个子类的特点:
(1)ArrayList:
- 底层数据结构是数组,查询快,增删慢。
- 线程不安全,效率高。
(2)Vector:
- 底层数据结构是数组,查询快,增删慢。
- 线程安全,效率低。
- Vector相对ArrayList查询慢(线程安全的)
- Vector相对LinkedList增删慢(数组结构)
(3)LinkedList:
- 底层数据结构是链表,查询慢,增删快。
- 线程不安全,效率高。
List接口三个子类的区别:
(1)Vector和ArrayList的区别
-
Vector是线程安全的,效率低
-
ArrayList是线程不安全的,效率高
-
共同点:都是数组实现的
(2)ArrayList和LinkedList的区别
- ArrayList底层是数组结果,查询和修改快
- LinkedList底层是链表结构的,增和删比较快,查询和修改比较慢
- 共同点:都是线程不安全的
三、Set接口
3.1 Set接口概述
Set 集合与 Colleaction 基本相同,没有提供额外的方法,实际上 Set 就是 Collection,只是行为略有不同(Set 不允许包含重复元素)。
集合中的元素不可重复,无索引,有没有序要看Set接口具体的实现类是谁。
Set接口常见的实现类有:HashSet、LinkedHashSet
- HashSet集合中元素的特点:无序不可重复
- LinkedHashSet集合中元素的特点:有序不可重复
3.1 HashSet
HashSet是基于HashMap实现,大多数时候使用 Set 集合时就是使用这个实现类。
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。
数据结构是哈希表。线程是非同步的。
(1)保证元素唯一性的方法:
-
判断元素的hashCode值是否相同。
-
如果相同,还会继续判断元素的equals方法,是否为true。
我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率, 降低了使用equals()方法的次数。
当HashSet调用add()方法存储对象的时候, 先调用对象的hashCode()方法得到一个哈希值, 然后在集合中查找是否有哈希值相同的对象。
如果没有哈希值相同的对象就直接存入集合。
如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行equals()比较,比较结果为false就存入, true则不存。
示例代码如下:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime*result + age;
result = prime*result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) //调用的对象和传入的对象是同一个对象
return true; //直接返回true
if (obj == null) //传入的对象为null
return false; //返回false
//判断两个对象对应的字节码文件是否是同一个字节码
if (getClass() != obj.getClass())
return false; //如果不是直接返回false
Person other = (Person) obj; //向下转型
if (age != other.age) //调用对象的年龄不等于传入对象的年龄
return false; //返回false
if (name == null) { //调用对象的姓名为null
if (other.name != null) //传入对象的姓名不为null
return false; //返回false
} else if (!name.equals(other.name))
return false; //返回false
return true; //返回true
}
(2)HashSet特点:
-
不能保证元素的排列顺序
-
HashSet不是同步的,如果多个线程同时来访问一个 HashSet,假设有两个或者两个以上线程同时修改了HashSet 集合时,则必须通过代码来保证其同步。
-
集合元素值可以是 null。
3.2 LinkedHashSet
LinkedHashSet是HashSet的子类,不允许集合元素重复,LinkedHashSet 集合也是根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。
(1)当遍历 LinkedHashSet 将会按照添加元素顺序来访问集合里的元素。
(3)LinkedHashSet 需要维护元素的插入顺序,因此性能略低于 HashSet 的性能,但在迭代访问 Set 里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。
(3)LinkedHashSet只能按照先后顺序来进行排序,TreeSet则是按照比较器给的比较规则进行从小到大排序。TreeSet实现是借助于TreeMap。
(4)LinkedHashSet的特点:
保证元素唯一。保证存储数据 。不安全。
3.3 SortedSet接口与TreeSet类
SortedSet接口是Set接口的子接口,除了拥有Set集合的一些基本特点之外,还提供了排序的功能。
TreeSet类就是SortedSet接口的实现类。
3.4 TreeSet
(1)TreeSet继承与实现关系
-
TreeSet 是一个有序的集合,它的作用是提供有序的Set集合。它继承于AbstractSet抽象类,实现了NavigableSet, Cloneable, java.io.Serializable接口。
-
TreeSet 继承于AbstractSet,所以它是一个Set集合,具有Set的属性和方法。
-
TreeSet 实现了NavigableSet接口,意味着它支持一系列的导航方法。比如查找与指定目标最匹配项。
-
TreeSet 实现了Cloneable接口,意味着它能被克隆。
-
TreeSet 实现了java.io.Serializable接口,意味着它支持序列化。
-
TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:
自然排序或者根据创建TreeSet 时提供的 Comparator (比较器排序)进行排序。这取决于使用的构造方法。
-
TreeSet是非同步的。
(2)TreeSet存储对象
TreeSet存储对象的时候, 可以排序, 但是需要指定排序的算法。
例如:Integer能排序(有默认顺序), String能排序(有默认顺序), 自定义的类存储的时候出现异常(没有默认顺序)
如果想把自定义类的对象存入TreeSet进行排序, 那么必须实现Comparable接口,重写compareTo()方法
在使用TreeSet存储对象的时候, add()方法内部就会自动调用compareTo()方法进行比较, 根据比较结果使用二叉树形式进行存储。
(3)TreeSet特点
TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列,同时也会保证对象的唯一。
TreeSet集合容器中保存对象的时候,只要将对象放到这个集合中,集合底层会自动的对其中的所有元素进行排序。当我们在取出的时候,元素全部排序完成。
TreeSet集合:它的底层使用二叉树(红黑树)结构。这种结构可以对其中的元素进行自然排序。
3.5 总结
(1)HashSet
- HashSet集合是Set接口的实现类。它保证元素不重复。
- HashSet底层使用的哈希表,不保证存取的顺序(迭代顺序)
- 保证对象不重复需要复写hashCode和equals方法。
- HashSet集合只能使用Iterator和foreach遍历,不能使用List接口的特有方法遍历。
- HashSet不安全。
(2)LinkedHashSet
-
HashSet的子类。它的底层接口是链表+哈希表结构。
-
保证对象唯一,不安全,可以保证元素的存取顺序。
(3)TreeSet
-
保证对象唯一
-
保存对象的时候,只要将对象放到这个集合中,集合底层会自动的对其中的所有元素进行排序。当我们在取出的时候,元素全部排序完成。
-
底层使用二叉树(红黑树)结构。这种结构可以对其中的元素进行自然排序。
四、Queue接口
4.1 Queue接口概述
用于模拟队列这种数据结构,然后该接口中声明了一些基本操作的方法。例如:add、offer、remove等。
**add(E)/offer(E):**入队,即向队尾追加元素,二者的区别在于如果队列是有界的,add方法在队列已满的情况下会抛出IllegalStateException,而offer方法只会返回false
**remove()/poll():**出队,即从队头移除1个元素,二者的区别在于如果队列是空的,remove方法会抛出NoSuchElementException,而poll只会返回null
**element()/peek():**查看队头元素,二者的区别在于如果队列是空的,element方法会抛出NoSuchElementException,而peek只会返回null
Deque接口常用方法:
-
addFirst(E) / addLast(E) / offerFirst(E) / offerLast(E)
-
removeFirst() / removeLast() / pollFirst() / pollLast()
-
getFirst() / getLast() / peekFirst() / peekLast()
-
removeFirstOccurrence(Object) / removeLastOccurrence(Object)
4.2 PriorityQueue
PriorityQueue保存队列元素的顺序不是按照加入队列的顺序,而是按队列元素的大小重新排序。
4.3 Deque接口
Deque代表一个双端队列,可以当作一个双端队列使用,也可以当作“栈”来使用,因为它包含出栈pop()与入栈push()方法。
Queue只能在队尾入队,队头出队,而Deque接口则在队头和队尾都可以执行出/入队操作
4.4 ArrayDeque
实现了Deque接口中定义的方法,是Deque的实现类。
五、Map接口
5.1Map接口概述
Map是一个映射接口,即key-value键值对。使用Entry保存每个key-value对,以实现通过key快速定位到对象(value)。每个键映射到一个值。但要注意的是:key不能重复。通过指定的key就可以取出对应的value。如果是自定义类型必须重写hashCode()和equals()方法。
(1)Map的实现主要包括:
HashMap, TreeMap, HashTable 和 LinkedHashMap:
- HashMap 使用哈希表(hash table)实现, 在 keys 和/或 values 之中,都是无序的.
- TreeMap 基于红黑树(red-black tree)数据结构实现, 按 key 排序.
- LinkedHashMap 保持者插入顺序.
- Hashtable 与HashMap实现方式一样,但Hashtable属于同步(synchronized)的.
如果代码是线程安全的,那么应该使用HashMap,因为Hashtable的同步是有一定量的运行代价的。
(2)Map接口和Collection接口的不同
- Map是双列的,Collection是单列的
- Map的键唯一,Collection的子体系Set是唯一的
- Map集合的数据结构值针对键有效,跟值无关;Collection集合的数据结构是针对元素有效。
- Map集合没有直接取出元素的方法,而是先转成Set集合,在通过迭代获取元素
(3)Map接口的主要方法包括:
- size() - 集合内的对象数量
- put(K,V)/putAll(Map) - 向Map内添加单个/批量对象
- get(K) - 返回Key对应的对象
- remove(K) - 删除Key对应的对象
- keySet() - 返回包含Map中所有key的Set
- values() - 返回包含Map中所有value的Collectio
- ·entrySet() - 返回包含Map中所有key-value对的EntrySet
- containsKey(K)/containsValue(V) - 判断Map中是否存在指定key/value等
Map中包含一个内部类:Entry。该类封装了一个键值对,它包含了三个方法:
-
Object getKey():返回该Entry里包含的key值。
-
Object getValeu():返回该Entry里包含的value值。
-
Object setValue(V value):设置该Entry里包含的value值,并返回新设置的value值。
5.2 Map实例之遍历
Map的遍历大体有3种:
第一种:遍历Map.entrySet():它的每一个元素都是Map.Entry对象,这个对象中,放着的就是Map中的某一对key-value。
第二种:遍历Map.keySet():它是Map中key值的集合,我们可以通过遍历这个集合来读取Map中的元素。
第三种:遍历Map.values():它是Map中value的集合,我们可以直接通过这个集合遍历Map中的值,却不能读取key。
5.3 HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。它存储的时候是无序的。
HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。
在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。
HashMap实际上是一个“链表的数组”的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。
HashMap注意事项:
1)HashMap底层维护一个数组,我们向HashMap中所放置的对象实际上是存储在该数组当中。
2)当向HashMap中put一对键值时,它会根据key的hashCode值计算出一个位置,该位置就是此对象准备往数组中存放的位置。
HashMap由于其快速寻址的特点,可以说是最经常被使用的Map实现类
5.4 Hashtable(不做要求)
Hashtable 可以说是HashMap的前身(Hashtable自JDK1.0就存在,而HashMap乃至整个Map接口都是JDK1.2引入的新特性),其实现思 路与HashMap几乎完全一样,都是通过数组存储Entry,以key的哈希值计算Entry在数组中的index,用拉链法解决哈希冲突。二者最大的不同在于,Hashtable是线程安全的,其提供的方法几乎都是同步的。
5.5 LinkedHashMap
LinkedHashMap与HashMap非常类似,唯一的不同在于前者的Entry在HashMap.Entry的基础上增加了到前一个插入和后一个插入的Entry的引用,以实现能够按Entry的插入顺序进行遍历。 特点:底层是链表实现的可以保证怎么存就怎么取
5.6 TreeMap
TreeMap是基于红黑树实现的Map结构,其Entry类拥有到左/右叶子节点和父节点的引用,同时还记录了自己的颜色。
红黑树实际是一种算法复杂但高效的平衡二叉树,具备二叉树的基本性质,即任何节点的值大于其左叶子节点,小于其右叶子节点,利用这种特性,TreeMap能够实现Entry的排序和快速查找。
TreeMap的Entry是有序的,所以提供了一系列方便的功能,比如获取以升序或降序排列的KeySet(EntrySet)、获取在指定key(Entry)之前/之后的key(Entry)等等。适合需要对key进行有序操作的场景。
线程不同步,可用于给Map集合中的键进行排序。
注意:
1)用作key的对象必须实现hashCode和equals方法。
2)不能保证其中的键值对的顺序
3)尽量不要使用可变对象作为它们的key值。
5.7 SortedMap接口和TreeMap类的关系
-
SortedMap接口是Map的子接口,其进一步提供对于键的排序功能。
-
TreeMap类就是SortedMap接口的实现类。
-
TreeMap可以对key值进行自然排序或者比较器排序,其用法和TreeSet是一致的。
5.8 HashMap与HashTable的区别
同步性
Hashtable是同步的,这个类中的一些方法保证了Hashtable中的对象是线程安全的。而HashMap则是异步的,因此HashMap中的对象并不是线程安全的。
因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用HashMap是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销,从而提高效率。
值
HashMap可以让你将空值作为一个表的条目的key或value,但是Hashtable是不能放入空值的。HashMap最多只有一个key值为null,但可以有无数多个value值为null。
性能
HashMap的性能最好,HashTable的性能是最差(因为它是同步的)