集合
一:集合应用
1.1:集合类架构图(掌握)
Collection是单列集合父接口。
List存取有序,有索引,能存储重复元素。
Set存取无序,无索引,按照某种方式。
Map是双列集合父接口。
1.2:常用方法(留个印象)
1.2.1:Collection(所有子类对象都能用该方法)
add(E e)
remove(E e)
size()
clear() //将集合清空
contains(E e)
toArray() //将集合转为数组
isEmpty() //集合是否为空
Iterator<E> iterator()
boolean containsAll(Collection<?> c)
boolean addAll(Collection<? extends E> c)
boolean removeAll(Collection<?> c)
1.2.2:List
a:List接口常用方法
add(int Index,E e);
remove(int Index);
get(int index);
b:LinkedList常用方法(头和尾相关方法)
public void addFirst(E e) :将指定元素插入此列表的开头。
public void addLast(E e) :将指定元素添加到此列表的结尾。
public E getFirst() :返回此列表的第一个元素。
public E getLast() :返回此列表的最后一个元素。
public E removeFirst() :移除并返回此列表的第一个元素。
public E removeLast() :移除并返回此列表的最后一个元素。
c:注意:
ListIterator<Integer> iterator = list1.listIterator();是list集合特有的。
1.2.3:Collections工具类常用方法
public static void shuffle(List<?> list) 打乱顺序 :打乱集合顺序。
public static <T> void sort(List<T> list) :将集合中元素按照默认规则排序。
public static <T> void sort(List<T> list,Comparator<? super T> ) :将集合中元素按照指定规则排
1.2.4:Map
public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。
public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的 值。
public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
public Set<K> keySet() : 获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
Entry键值对对象
public K getKey() :获取Entry对象中的键。
public V getValue() :获取Entry对象中的值。
1.3:集合遍历(理解)
1.3.1:List(ArrayList,LinkedList)集合遍历
- for循环 for (int i = 0; i < list.size(); i++){}
a:删除遍历过程中元素园路
在遍历的时候可以进行元素删除。但是每次删除一个一个元素的时候,该元素之后的元
素下标就会向前移动一位,所以在删除一个元素,让i值进行减一。采用倒叙遍历就可以解
决这个问题。
b:实战,去除集合重复元素
例子1:
List<String> list = new ArrayList();
//从头取出第一个元素,然后和后面的元素对比,一样的删除。
for (int i = 0; i < list.size() -1; i++) {
for (int c = list.size() - 1; i > i; c--) {
if (list.get(i).equals(list.get(c))) {
list.remove(c);
}
}
}
例子2(链表,解题思路适合链表结构):
List<String> list = new LinkedList();
//判断集合中是否含有元素,含有,删除。然后再判断集合中是否还含有刚才删除的元素,含有,代表该元素重复了。没有了,代表该元素没有重复,将该元素添加到集合中。
for (int i = list.size() -1; i > 0 ; i--) {
if(list.contains(list.get(i))){
Integer remove = list.remove(i);
if(!list.contains(remove)){
list.add(i,remove);
}
}
}
- 增强for循环(底层是遍历器)
- 迭代器
a:遍历原理:
迭代器遍历集合的时候(遍历开始的时候,指针指向第一个元素前),先判断该指针后是否
含有元素,如果含有指针向后移动一位,然后取出元素,然后再次判断指针后是否含有元
素,如果含有元素取出元素,没有元素遍历结束。
b:遍历过程中删除元素
在遍历的过程中删除元素,不能使用集合的删除方法,得使用迭代器的删除方法(iterator.remove();)。要是并发条件需要对Iterator对象加锁。
实例:
while (iterator.hasNext()) {
iterator.next();
iterator.remove();
}
//iterator.hasNext(),集合中是否含有元素
//iterator.next();取出元素,将指针向后移动一位
//迭代器遍历元素的时候,指针指向头,不指向任何元素,所以在在删除时候,必须调用next()元素。
c:注意:
在遍历集合,集合和迭代器同时持有同一个对象,当集合在添加,和删除集合元素时(修改呢),迭代器并不知道,
所以会发生并发修改异常。(迭代器遍历和增强for循环)
1.3.2:Set集合遍历
- 迭代器(和List遍历一样)
- 增强for循环(和List遍历一样)
1.3.3:Map集合遍历
例一:
// 获取 所有的 entry对象 entrySet
Set<Entry<String,String>> entrySet = map.entrySet(); // 遍历得到每一个entry对象
for (Entry<String, String> entry : entrySet) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"的CP是:"+value);
}
例二:
Set<String> keys = map.keySet(); // 遍历键集得到每一个键
for (String key : keys) { //key 就是键
//获取对应值
String value = map.get(key);
System.out.println(key+"的CP是:"+value);
}
二:集合原理
2.1:ArrayList和Vector(理解)
- ArrayList
底层是数组实现的,线程不安全、效率高。
初始化容量默认是10,在新增每一个元素都会对索引正确性进行判断,是否扩容进行判断。集合进行扩容的时候,就
是使用arraycopy(Object src, int srcPos,Object dest, int destPos,int length),方法进行数组
复制。删除元素时,判断索引是否和法,原理是把被删除元素右边的元素左移,方法同样是使用System.arraycopy
进行拷贝。修改元素仅仅需要检查元素下标。
多个线程对同一个ArrayList集合进行访问,至少至少又一个线程改变集合结构,所以为了保证数据同步List<Integer>
list = Collections.synchronizedList(new ArrayList<Integer>());
Collections.synchronizedList()优势:
Vector对大部分方法都加锁了,效率很低。
Collections.synchronizedList(new ArrayList<Integer>()),仅仅在新增编辑删除加锁,对遍历的时候没加锁。
- Vector
底层数组。大部分方法都使用了synchronized修饰,线程安全,效率低。
2.2:LinkedList(理解)
2.2.1:底层数据结构(双链表结构)
是线程不安全的。
在进行删除,遍历找到元素,删除元素并修改前后元素索引。
在新增时候,遍历找到指定索引,添加元素,修改前后元素索引。
2.3:HashSet、TreeSet和LinkedHashSet(理解)
2.3.1:HashSet
a:底层数据结构:
底层就是HashMap,原理图看HashMap原理图。
b:添加元素流程
c:注意
当集合中存储的是系定义对象,要重写hashCode()方法和eques()。
2.3.2:TreeSet(了解)
a:原理
TreeSet底层数据结构基于TreeMap,红黑树。
可以按照自然顺序,也可以自定义排序。(和HashSet去重不一样)
b:自然顺序(Comparable)
TreeSet类的add()方法中会把存入的对象提升为Comparable类型
调用对象的compareTo()方法和集合中的对象比较(返回0,不保存元素)
根据compareTo()方法返回的结果进行存储
c:比较器顺序(Comparator)
创建TreeSet的时候可以制定 一个Comparator
如果传入了Comparator的子类对象, 那么TreeSet就会按照比较器中的规则比较
add()方法内部会自动调用Comparator接口中compare()方法排序(返回0,不保存元素)
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
2.3.3:LinkedHashSet
底层是链表+哈希表,相对于HashSet,存取有序。
2.4:HashMap、HashTable(理解)
2.4.1:HashMap
a:HashMap底层图
默认初始容量为16(为2的n次方,减少hash碰撞,让数据散列更加均匀),负载因子0.75。
当新增元素超过初始容量*负载因子,则进行扩容,2倍进行扩容。
是线程不安全。
注意:
HashMap集合中key和value都可以为null,当key值一样时候,value值会覆盖。
HashTable是都不能为null。
TreeMap是key不能为null,value可以为null。
2.4.2:HashTable(被HashMap取代)
是线程安全的。底层数据结构和HashMap一致。
HashTable里使用的是synchronized关键字,这其实是对对象加锁,锁住的都是对象整体,
当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的
时间。
2.4.3:ConcurrentHashMap(重点、重点、重点)
a:内部结构
ConcurrentHashMap类包含两个静态内部类,HashEntry和Segment。HashEntry用来封装
映射键值对。Segment用来充当锁的角色,每一个Segment守护散列映射表的若干个桶。
每个桶由若干个Segment组成。每一个ConcurrentHashMap的实例,由若干个Segment对
象组成数组。
b:描述
和HashMap功能基本一致,主要为了解决HashMap线程不安全问题。
JDK7基本设计理念就是切分成多个Segment块,默认是16个。每个Segment里面可以近似
看成一个HashMap,每个Segment块都有自己独立的ReentrantLock锁。java8中Segment
块换成了Node,每个Node有自己的锁,即每个Node都有自己的并发度。
2.5:LinkedHashMap和TreeMap(理解)
2.5.1:LinkedHashMap
底层数据结构是,链表+哈希表。
2.5.2:TreeMap
TreeMap底层是红黑树结构,其实也就是平衡二叉树,方便快速找到指定节点。
红黑树的插入、删除和新增的时间复杂度都是0(lgn),性能远低于hash表,但是能够按照键
值大小有序输出。