一、学习集合前的基础概念
1.1加载因子
比如说哈希表,假如加载因子是0.75,则意味着当哈希表存储的元素的占总哈希表的空间大于等于0.75时,就会重新建立一个容量更大的哈希表,将原有的元素放到新的哈希表中,原有的哈希表删除。加载因子的本质就是判断是否需要扩展存储空间的标志。
1.2 哈希表
数组和链表的集合体。通过一个hash函数计算出元素的哈希值,再对数组的长度取余,将该元素放到取余结果对应的位置。
1.3 哈希表的特点
数组的特点是查找易,增删难,链表的特点是增删易,查找难,哈希表利用了数组查找易,链表增删易的特点。相对来说使查找和增删的功能相对平衡一些。
- 查询效率:最优的情况下可以达到o(1)
- 由于哈希表是基于数组的,所以扩展空间难
1.4 equals方法为true,哈希值一定相同吗
一定,因为java中规定,对象相等,必须具有相同的哈希码值。
1.5 数组的查找效率
- 通过下标查找,时间复杂度是o(1)
- 通过值比较查找,时间复杂度是o(N)
1.6 泛型的作用
- 为什么不用Object类
因为Object类不是类型安全的,且如果要进行类型检查,需要进行强制类型转换,可能会导致异常。而且可能会有装箱,拆箱的操作。而泛型则不需要装箱和拆箱的操作。从而提高程序性能。 - 泛型相对来说比较安全,且在编译时期,就会替换成指定给的类型。实现泛型的方法称为泛型擦除。
二、一些树的概念
2.1 二叉排序树
树的左子树小于根节点的值,树的右子树的值大于根节点的值,且树的左子树,右子树都是二叉排序树。
2.1 平衡二叉树(AVL树)
首先是一颗二叉排序树,且树的左右子树的高度不超过1。且左右子树也是平衡二叉树。
2.3 平衡因子
树的左子树的深度减去右子树的深度可能的值,平衡二叉树的平衡因子可能是-1,0,1。
2.4 红黑树
- 叶子结点到根节点的所路径的黑色节点的数目是相同的。
- 根节点和叶节点是黑的。
三、迭代器
3.1 使用迭代器的好处
- 作用:对集合遍历的方法抽象出来,隐藏集合的内部结构
3.2 注意
使用迭代器的时候,集合的大小不能概念。
3.3 并发异常
- 在遍历集合的过程中,如果使用集合本身的增,删方法操作集合,会抛并发修改异常。
- 在迭代器遍历之前,有两个参数ModCount和ExpectModCount被初始化为0,并且,只有在增加或者删除集合中的元素的时候,ModCount的值会增加1,在使用next()方法时会判断ModCount和ExpectModCount的值是不是相等,不等的话会抛出并发修改异常。
3.4 并发异常解决方法
- 使用迭代器自带的增,删方法可以添加数据,但是在本轮遍历中并不会遍历到。
- 使用iterator的删除方法删除数据,使用lsitIterator的add方法增加数据,会在下一轮显示出集合的变化。
- 一个迭代器使用完之后,指针就到了集合的尾部,再次使用迭代器,需要新建一个迭代器对象使用。
public class order {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(3);
list.add(3);
int count = 0;
ListIterator<Integer> a = list.listIterator();
while (a.hasNext()){
Integer next = a.next();
System.out.print(next);
}
}
}
- 使用普通的for方法
- 将集合转换为普通的数组进行遍历
3.5 iterable接口
实现了iterator接口,就可以使用新式for循环。
3.6 自定义迭代器
3.7 Spliterator迭代器
将遍历的任务交由多个线程去处理。
3.8 ListIterator迭代器
- ListIterator可以双向遍历集合,Iterrator只能单项遍历集合。可以向前遍历。
- ListIterator可以使用set()方法对元素进行修改。
3.9 迭代器和枚举方式遍历集合的区别
- 使用迭代器可以在遍历的过程中删除元素
- 方法名较短
四、集合类
4.1 特点(和数组的比较)
-
动态的增加集合的容量
-
只能存储引用数据类型
-
集合在java.util包下
-
可以使用Readonly方法实集合是只读的,但是数组不可以
4.2 组成
Map,Collection,迭代器
Collection包括Set,List,Queue
4.3 Collection接口
- 重写了toString()方法
4.4 常用的集合类
- Collections
操作集合的供给类
4.4.1 list类
- ArrayList
查找效率高的list - LinkedList
增删效率高的list
4.4.2 set类
- HashSet
没有重复元素的无序集合 - LinkedHashSet
可以记住元素插入顺序的集合 - TrssSet
有序的集合
4.4.3 Map类
- HashMap
只允许一条记录的键位null,允许多条记录的值为null,非同步的。 - TreeMap
把保存的值按照key来排序,默认是升序,不允许key为null,非同步的。 - HashTable
不允许key和value为null,同步的,多线程环境下效率较低。 - LinkedHashMap
保存数据插入的顺序,key和value允许为空,非同步的。
五、List集合类相关
5.1 Array类
- 以连续的内存存储数据,不支持对数据的删除,增加,迭代等操作。
- Array的大小是固定的。
- 只能存储同构的对象
- 能够对自身枚举
- 可以存储基本类型数据和引用类型数据
5.2 Arrays工具类
- 查看数组数组中是否有特定的值
注意:下面的方法只适合原来数组中存放的是引用数据类型
public class order {
public static void main(String[] args) {
Integer[] ints = new Integer[]{1,3,4,2};
List<Integer> integers = Arrays.asList(ints);
System.out.println(integers.contains(3));
}
}
5.3 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 max(Collection coll)
根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll) -
int frequency(Collection c, Object o)
统计元素出现次数
public class order {
public static void main(String[] args) {
Integer[] ints = new Integer[]{1,3,4,2};
List<Integer> integers = Arrays.asList(ints);
Collections.rotate(integers,2);
for(Integer i : integers){
System.out.print(i + " ");
}
}
}
Collections集合类将集合包装成线程安全的集合
SynchronizedList(List);
SynchronizedSet(Set;
SynchronizedMap(Map);
5.4 List的特点
- 保证元素的顺序
- 允许元素重复
- 特有方法有索引
5.5 Arrayiist集合的特点
- 底层:数组,查找时使用get方法查找,是方法调用,比原生的数组查找效率低。
- 元素是否有序:有序
- 元素类型:引用数据类型
- 是否安全:线程不安全的。
- 特点:查找快,增删慢
- 动态扩展:按照原大小的1.5倍扩展
- 缺点:由于会在数组后面预留一定的空间,所以会造成空间的浪费。
- 对null元素包容性:允许有多个null值
- 是否具有索引:具有索引
如果要保证增删是安全的,可以使用CopyOWriteArrayList或者使用collections.synchronizedList(Lise l)函数返回一个线程安全的ArrayList类。
list中:Iterator iterator();这个方法内有加锁,所以在下面需要加锁。
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
参考:Collections.synchronizedList使用
5.6 为什么ArrayList的elementData是用transient修饰的?
transient修饰意味着不会被序列化,因为集合中规定elementData不总是满的,如果序列话的话需要浪费很多的时间和空间,即不使用Java默认的序列化机制来实例化,自己实现了序列化writeObject()和反序列化readObject()的方法。
如果知道元素过多,可以提前通过ensureCapacity()方法进行扩容。
建议在单线程中使用ArrayList
5.7 Linkedlist集合
- 底层:双向循环链表
- 线程安全:线程不安全
- 元素有序:元素有序
- 特点:增删快,查找慢
- 继承关系:LinkedList是一个继承于AbstractSequentialList的双向链表
- Null:允许有null元素
- Fail-Fast机制:使用一个ModCount参数实现这个机制,当ModCount参数变化时,迭代器很快完全失败。
- For循环:对普通的For循环没有友好的支持
- 实现Dqueue,Queue接口:可以作为队列或者双端队列。
5.8 Vector集合
- 底层:底层的数据结构是数组
- 线程安全:线程安全
- 元素有序:元素有序
- 特点 :增删慢,查找慢
- 扩容:2倍扩容
- 实现接口: 实现了RandmoAccess接口,即提供了随机访问功能。实现了Cloneable接口,即实现clone()函数。它能被克隆
- 遍历:使用迭代器遍历,注意要上锁
六、set集合类相关
6.1 set集合
- 底层:map集合
- 元素重复:元素不重复
- 元素有序:元素无序
- set去重功能:重写了equals和hashcode()方法
- 索引:不支持索引的功能
- null的支持:只允许有一个null值
6.2 HashSet集合
底层:HashMap
线程安全:不安全
元素有序:无需
null:允许null元素
特点:存储和取出比较快
6.3 LinkedHashSet集合
- 底层:链表+哈希表
- 元素顺序:内部使用链表维护元素的插入顺序
6.4 TreeSet集合
- 底层:二叉树
- 线程安全:
- null:由于底层是二叉树,所以不允许元素为空
- 元素重复:不允许元素重复
- 元素有序:元素有序且默认升序
6.4.1 TreeSet的排序方式
- 实现Comparable接口,覆盖compareTo方法
- 定义一个Comparator比较器,并覆盖compare方法
实现比较器:
public class test1 {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Stu stu1 = (Stu)o1;
Stu stu2 = (Stu)o2;
return stu1.getAge() - stu2.getAge();
}
});
treeSet.add(new Stu("张三",12));
treeSet.add(new Stu("李四",2));
treeSet.add(new Stu("王五",22));
for(Object stu : treeSet) {
Stu stu1 = (Stu) stu;
System.out.println(stu1.getName() + " " + stu1.getAge());
}
}
}
实现Comparable接口:
public class Stu{
String name;
Integer age;
.........
@Override
public int compareTo(Object o) {
Stu stu = (Stu)o;
return this.getName().compareTo(stu.getName());
}
}
6.4.2 TreeSet常用方法
-
first():返回第一个元素
-
last():返回最后一个元素
-
lower(Object o):返回指定元素之前的元素
-
higher(Obect o):返回指定元素之后的元素
-
subSet(fromElement, toElement):返回子集合
七、Map集合
7.1 Map集合的特点
- 元素:主要存储键值对
- 重复:key不能重复,value可以重复
- 底层:哈希表
- 元素有序:元素无序
- 继承关系:map没有继承Collection接口
7.2 Map集合的遍历方式
- 通过key找value
Set keySet();得到一个map中存放所有key的集合
public class test1 {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("1","张三");
map.put("2","李四");
map.put("3","王五");
Set<String> strings = map.keySet();
for(String str : strings){
System.out.println(map.get(str) + " " + str + " ");
}
}
}
- Set<Map.Entry<k,v>> entrySet();
得到map集合的key-value视图
public class test1 {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("1","张三");
map.put("2","李四");
map.put("3","王五");
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries){
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
7.3 HashMap集合
- 底层:数组和链表
- 安全:不安全
- 增删改查:增删查速度较快
- null:允许一个键为null,多个值为null
- 初始大小:16
- 扩容:2的幂数扩容
- 接口:实现了Serializable和Clonable借接口
数组:键和值作为一个Entry对象存在数组中,JDK1.7的HashMap底层是数组+链表,JDK1.8的HashMap是数组+链表+红黑树,当阀值大于8时,链表转换为红黑树,当阀值小于6时,红黑树转换为链表
- HashMap多线程put操作会出现死循环
7.4 HashTable集合
- 底层:数组+链表
- 线程安全:线程安全
- 父类:dictionary
- null:null不能为空,原因是在源码中,Entry对象需要调用equals方法,如果为null,会抛出异常
- 初始容量: 11
- 扩容:2倍+1
7.5 Properties集合
- 底层:哈希表
- key,value:内置string类型
读取配置文件步骤:
- 新建Properties对象
- 新建FileReader对象,绑定数据源
- 使用Load方法加载数据
Store方法:将数据持久化到硬盘中
7.6 LinkedHashMap集合
- 顺序:维护数据的顺序,内部有一个双向链表
- LRU特性
7.7 TreeMap集合
- 底层:红黑树
- 接口:实现SortMap接口
- 线程安全:线层不安全
- 特点:基本操作的时间复杂度是log(n)
- 遍历:EntrySet方式遍历,时间复杂度是o(1),keySet方式遍历,时间复杂度是o(N)
实现比较器排序:根据key排序
public class test1 {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
map.put(8,"张三");
map.put(5,"李四");
map.put(9,"王五");
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for(Map.Entry<Integer, String> entry : entries){
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
根据value排序:
public class test1 {
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(8,"dddd");
map.put(5,"aaaa");
map.put(9,"cccc");
ArrayList<Map.Entry<Integer, String>> entries = new ArrayList<>(map.entrySet());
Collections.sort(entries, new Comparator<Map.Entry<Integer, String>>() {
@Override
public int compare(Map.Entry<Integer, String> o1, Map.Entry<Integer, String> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
for(Map.Entry<Integer, String> entry : entries){
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
7.8 使用Java8新增的Predicate操作集合
Java 8为Collection集合新增了removeIf(Predicate filter)方法,该方法将会批量删除符合条件的filter条件的所有元素