集合
认真、细致。
此内容在学习过程中不断修改,仅供自己复习使用。
一、简介
1. 继承关系
Iterable
|
Collection -------------- Map ------------ Iterator
| ------------------------------ | -------------------- |
List & Set & Queue — SortedMap — ListIterator
2. Collection接口
在JAVA类库中,集合类的基本接口是Collection接口。这个接口有两个基本方法:
public interface Collection<E>{
boolean add(E element);
Iterator<E> iterator(); //用于返回一个实现了Iterator接口的对象,可以遍历集合,详见五
...
}
二、List
List为有序、允许重复、线程不安全的集合
两种访问方式:迭代器、索引
1. ArrayList
动态数组:通过数组的复制实现
2. LinkList
Java中的链表都是双向链表
3. Vector
Vector为线程安全的集合
Vector 类的所有方法都是同步的。可以由两个线程安全地访问一个 Vector 对象。但是 ,如果由一个线程访问Vector , 代码要在同步操作上耗费大量的时间。 这种情况还是很常见的 。而 ArrayList 方法不是同步的,因此, 建议在不需要同步使用ArrayList , 而不要使用 Vector。
三、Set
1. 简介
Set为无序、不允许重复、线程不安全的集合。
使用了散列表相关概念。
- 可以理解为建议版的HashMap
- 底层核心
private transient HashMap<E,Object> map;
- 其中,E为set集合的key,Object为
private static final Object PRESENT = new Object();
2. 何为无序?
-
为什么说Set是无序的?
-
无序指Set集合的添加顺序与存储顺序不一致。
-
Set底层采用Map集合的key值存储,通过计算hash()值来进行存储(如何存储见Map),故存储顺序与添加顺序没有半毛钱关系。
-
部分源码:
-
private transient HashMap<E,Object> map; //Set的存储空间 private static final Object PRESENT = new Object(); //作为Map的value的对象 public boolean add(E e) { return map.put(e, PRESENT)==null; }
-
四、Map
Map的两个常用实现:HashMap(键值映射)、TreeMap(树映射、搜索树)
1. HashMap
-
HaspMap底层实现采用了哈希表。处理散列冲突的方法为“链地址法”。
-
核心数组
transient Node<K,V>[] table;
,也称之为“位桶数组” -
JDK8以后,当链表长度大于8时,链表就转为红黑树,这样提高了查找效率。
-
HashMap线程不安全、效率高,允许key或value为null
-
HashTbale线程安全、效率低、不允许key或value为null
-
/*HashMap类的部分代码*/ // aka 16,默认数组大小,数组大小为2的整数幂 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //核心数组,数组大小为2的整数幂 transient Node<K,V>[] table; //Node部分代码 static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; } //hash值得计算方法 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
2. HashMap存储键值对过程
- hashCode()方法是Object类中的方法
3. HashMap获取数据get()
- 1)获得key的hashCode,通过hash()散列算法得到hash值,进而定位到数组位置。
- 2)在链表上一个一个比较key对象,通过equals()方法将key对象和链表上结点的key对象进行比较。
- 3)返回equals()为true的结点对象的value对象
hashCode()方法和equals()方法的联系:
JAVA规定,两个内容相同(equals()为true)的对象必须具有相同的hashCode。如果equals()为true而hashCode不同,那么在整个存储过程中就发生了悖论。
4. 扩容问题
- HashMap的位桶数组,初始大小为16。实际使用时,显然大小是可变的。如果位桶数组中的元素达到(0.75*数组长度),就要重新调整数组大小。
- 扩容很耗时间,其本质是定义新的数组,并且拷贝旧数组。
5. TreeMap
- HashMap和TreeMap区别:TreeMap一般不用,在需要排序是才使用TreeMap
- TreeMap底层实现是红黑二叉树。
- 自定义对象使用TreeMap存储时,若需要排序,需要实现Compareable接口
五、迭代器和遍历
1. 简介
Iterator接口包含四个方法:
public interface Iterator<E>{
E next();
boolean hasNext();
void remove();
default void forEachRemaining(Consumer<? super E> action);
//注:jdk8以后允许在接口中定义静态方法和默认方法
/*
接口中的默认方法不要求实现,所有继承该接口的类也继承了该方法
*/
}
使用Iterator遍历集合的伪代码
Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while(iter.hasNext()){
String element = iter.next();
//do something with element
}
//foreach循环
for(String str:c){
//do something with str
}
编译器简单的将foreach循环翻译为带有迭代器的循环。for each循环可以与任何实现了Iterable接口的对象一起工作,这个接口只包含一个抽象方法即iterator()
,而Collection接口继承了Iterable接口,因此,标准库中任何集合都可以使用for each循环。
还可以直接用iterator.forEachRemaining(element -> do something with element)
直接遍历
2. 迭代器遍历List和Set
//Set和List一样
public static void testList(){
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String temp = iterator.next();
System.out.println(temp);
}
}
3. 迭代器遍历Map
方式一:通过map的实体对象
public static void testMap1(){
Map<Integer, String> map = new HashMap<>();
map.put(1, "aa");
map.put(2, "dd");
map.put(3, "gg");
Set<Map.Entry<Integer, String>> set = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator = set.iterator();
while(iterator.hasNext()){
Map.Entry<Integer, String> entry = iterator.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
方式二:通过map的key集合
public static void testMap2(){
Map<Integer, String> map = new HashMap<>();
map.put(1, "qq");
map.put(2, "cc");
map.put(3, "pp");
Set<Integer> set = map.keySet();
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()){
Integer temp = iterator.next();
System.out.println(temp + ":" + map.get(temp));
}
}
4. 增强for循环遍历
- 编译器简单的将foreach循环翻译为带有迭代器的循环。for each循环可以与任何实现了Iterable接口的对象一起工作,这个接口只包含一个抽象方法即
iterator()
,而Collection接口继承了Iterable接口,因此,标准库中任何集合都可以使用for each循环。
5. forEachRemaining遍历
- 暂不阐述
六、 Collections工具类
类java.util.Collections提供了Set、List、Map进行排序、填充、查找元素的辅助方法
-
void sort(List)
//对List容器内的元素升序排序(自定义的类使用:Comparable接口) -
void shuffle(List)
//对List容器内的元素随机排列(并非升序或降序),shuffle:调动、把…变换位置 -
void reverse(List)
//对List容器内的元素逆序排序(并非降序,而是对原序列反转) -
void fill(List, Object)
//用一个特定的对象重写List容器
行排序、填充、查找元素的辅助方法 -
void sort(List)
//对List容器内的元素升序排序(自定义的类使用:Comparable接口) -
void shuffle(List)
//对List容器内的元素随机排列(并非升序或降序),shuffle:调动、把…变换位置 -
void reverse(List)
//对List容器内的元素逆序排序(并非降序,而是对原序列反转) -
void fill(List, Object)
//用一个特定的对象重写List容器 -
int binarySearch(List, Object)
//对于顺序的List容器,采用折半查找法查找对象