#Java集合框架
##一, 集合体系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nARBLNmh-1618037485320)(D:\desktop\CommonWork\2021笔记文件\img\集合img\集合体系.png)]
##二,List Set Map 的区别
-
List 有序的可重复的
-
ArrayList
底层使用Object[]
- 查询快增删慢 线程不安全 效率高
- 支持随机访问
- 使用
RandomAccess
标识
- 使用
- 并发修改异常
- 支持随机访问
- 扩容机制
- 每次扩容为1.5倍
- 遍历异常
- 使用
foreach
和迭代器Iterator
遍历是不能添加和修改
- 使用
ArrayList
实现了Cloneable
方法和Serializable
- 查询快增删慢 线程不安全 效率高
-
Vector
底层也是object[]
- 方法级别线程安全 效率低
- 可使用CopyOnWriteArrayList替代
-
LinkedList 底层使用链表
- 查询慢增删快 线程不安全 效率高
- 双向链表 jdk1.6之前循环链表 1.7取消了循环链表
-
####
List
如何遍历 -
//对象遍历 @Test public void Test02(){ ArrayList<student> list02 = new ArrayList<>(); student s1 = new student("小明",15); student s2 = new student("小红",12); student s3 = new student("小芳",15); list02.add(s1); list02.add(s2); list02.add(s3); System.out.println(list02); for (int i = 0; i < list02.size(); i++) { System.out.println(list02.get(i).getName() +""+","+list02.get(i).getAge()); } Iterator<student> iterator = list02.iterator(); while(iterator.hasNext()){ student s = iterator.next(); System.out.println(s.getName()+""+s.getAge()); } for (student student : list02) { System.out.println(student.getName() +student.getAge()); } }
-
-
-
Set 无序的不可重复的
-
Map 通过key-value键值对存储一一对应,key无序不可重复,value可以重复。
-
HashMap
- 底层使用数组+链表
- 可以存储
null
但是只能有一个null
。 - 初始容量是 16 每次扩容 2 的幂次方 。
JDK1.8
之后当链表长度大于 8 时就转换为红黑树 ( 当数组长度小于64,先进行数组扩容)- 相比较与
HashSet
使用扰动函数(n - 1) & hash
减少hash碰撞(n - 1) & hash
也是由于这个所以HashMap
每次扩容都需要2的幂次方
HashMap
死循环- 当并发环境中其不是线程安全 ,
Rehash
就会出现循环链表。所以建议使用ConcurrentHashMap
- 当并发环境中其不是线程安全 ,
- 可以存储
- 底层使用数组+链表
-
TreeMap
-
底层使用红黑树来实现有序和不可重复 。
-
不能添加
null
。 -
添加的元素必须是可以比较的,以实现有序性。
-
┌───┐ │Map│ └───┘ ▲ ┌────┴─────┐ │ │ ┌───────┐ ┌─────────┐ │HashMap│ │SortedMap│ └───────┘ └─────────┘ ▲ │ ┌─────────┐ │ TreeMap │ └─────────┘
-
注意到
Person
类并未覆写equals()
和hashCode()
,因为TreeMap
不使用equals()
和hashCode()
-
特点
-
SortedMap
在遍历时严格按照Key的顺序遍历,最常用的实现类是TreeMap
;作为
SortedMap
的Key必须实现Comparable
接口,或者传入Comparator
;要严格按照
compare()
规范实现比较逻辑,否则,TreeMap
将不能正常工作。
-
-
-
Hashtable
- 线程安全方法级别的
synchronized
。 - 建议使用
ConcurrentHashMap
。 - 初试容量 11 。 2n+1 倍扩容
- 不能添加
null
。
- 线程安全方法级别的
-
Map遍历方式
-
public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "a"); map.put(2, "b"); map.put(3, "ab"); map.put(4, "ab"); map.put(4, "ab");// 和上面相同 , 会自己筛选 System.out.println(map.size()); System.out.println("第一种:通过Map.keySet遍历key和value:"); for (Integer in : map.keySet()) { //map.keySet()返回的是所有key的值 String str = map.get(in);//得到每个key多对用value的值 System.out.println(in + " " + str); } System.out.println("第二种:通过Map.entrySet使用iterator遍历key和value:"); Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, String> entry = it.next(); System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } // 第三种:推荐,尤其是容量大时 System.out.println("第三种:通过Map.entrySet遍历key和value"); for (Map.Entry<Integer, String> entry : map.entrySet()) { //Map.entry<Integer,String> 映射项(键-值对) 有几个方法:用上面的名字entry //entry.getKey() ;entry.getValue(); entry.setValue(); //map.entrySet() 返回此映射中包含的映射关系的 Set视图。 System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } System.out.println("第四种:通过Map.values()遍历所有的value,但不能遍历key"); for (String v : map.values()) { System.out.println("value= " + v); } }
-
使用那种呢
- 所以通过字节码来看,使用
EntrySet
和KeySet
代码差别不是很大,并不像网上说的那样KeySet
的性能远不如EntrySet
,因此从性能的角度来说EntrySet
和KeySet
几乎是相近的,但从代码的优雅型和可读性来说,还是推荐使用EntrySet
。
- 所以通过字节码来看,使用
-
修改
-
我们不能在遍历中使用集合
map.remove()
来删除数据,这是非安全的操作方式,但我们可以使用迭代器的iterator.remove()
的方法来删除数据,这是安全的删除集合的方式。同样的我们也可以使用 Lambda 中的removeIf
来提前删除数据,或者是使用 Stream 中的filter
过滤掉要删除的数据进行循环,这样都是安全的,当然我们也可以在for
循环前删除数据在遍历也是线程安全的。 -
// 根据 map 中的 key 去判断删除 map.keySet().removeIf(key -> key == 1); map.forEach((key, value) -> { System.out.println("show:" + key); });
-
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, String> entry = iterator.next(); if (entry.getKey() == 1) { // 删除 System.out.println("del:" + entry.getKey()); iterator.remove(); } else { System.out.println("show:" + entry.getKey()); } }
-
-
-
三,comparable
和 Comparator
-
实现对对象的自定义排序
-
comparable
-
public class Person implements Comparable<Person> { private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * T重写compareTo方法实现按年龄来排序 */ @Override public int compareTo(Person o) { if (this.age > o.getAge()) { return 1; } if (this.age < o.getAge()) { return -1; } return 0; } }
-
Comparator
-
ArrayList<Integer> arrayList = new ArrayList<Integer>(); arrayList.add(-1); arrayList.add(3); arrayList.add(3); arrayList.add(-5); arrayList.add(7); arrayList.add(4); arrayList.add(-9); arrayList.add(-7); System.out.println("原始数组:"); System.out.println(arrayList); // void reverse(List list):反转 Collections.reverse(arrayList); System.out.println("Collections.reverse(arrayList):"); System.out.println(arrayList); // void sort(List list),按自然排序的升序排序 Collections.sort(arrayList); System.out.println("Collections.sort(arrayList):"); System.out.println(arrayList); // 定制排序的用法 Collections.sort(arrayList, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2.compareTo(o1); } }); System.out.println("定制排序后:"); System.out.println(arrayList);
-
说明
- 定制排序也可以另外写一个类继承
ComparaTo
创建排序方式 - 使用上可以直接在创
ArrayList
时候直接传入ComparaTo
类 - 另外如何遇到
TreeSet
TreeMap
有序集合,必须将自己的Bean
对象实现comparable
或者ComparaTo
实现对象可比较。
- 定制排序也可以另外写一个类继承
-
-
无序性和不可重复性
- 无序性
- 存储元素不在根据添加顺序而是根据自身的hash值
- 不可重复性
- 当元素的
equals()
方法,返回false
,则需要同时重写equals()
和hashcode()
方法。
- 当元素的
- 无序性
三,线程安全的集合接口
-
线程安全的接口
Concurrent
-
实现类
ConcurrentHashMap
-
分段的数组+链表
-
原理
- 首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
-
实现方式
-
ConcurrentHashMap
是由Segment
数组结构和HashEntry
数组结构组成。Segment
实现了ReentrantLock
,所以Segment
是一种可重入锁,扮演锁的角色。HashEntry
用于存储键值对数据。
-
-
四, Collections
工具类
-
常用方法
-
排序
-
void reverse(List list)//反转 void shuffle(List list)//随机排序 void sort(List list)//按自然排序的升序排序 void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑 void swap(List list, int i , int j)//交换两个索引位置的元素 void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。 //当distance为负数时,将 list的前distance个元素整体移到后面
-
-
查找,替换操作
-
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的 int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll) int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c) void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。 int frequency(Collection c, Object o)//统计元素出现次数 int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target). boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素
-
-
同步控制(不推荐,需要线程安全的集合类型时请考虑使用 JUC 包下的并发集合)
-
##五,集合脑图