Collection
- Collection接口的实现类只能存储引用类型,所以对于基本数据类型,Collection的实现类存储的是它们的包装类(位于java.lang包下)
- 除了 int 对应 Integer,char 对应 Character,其它基本数据类型首字母大写即是其包装类类名。
Set
- 元素无序且不重复
HashSet
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
//添加元素
set.add(1);
set.add(2);
set.add(3);
//移除元素
set.remove(2);
//获取元素个数
int size = set.size();
//清空集合
set.clear();
//判断元素是否存在
boolean judge = set.contains(1);
}
}
List
- 元素有序可重复
ArrayList
- 基于动态数组的数据结构。
- 以无参数构造方式创建ArrayList时,实际上初始化赋值的是一个空数组。添加第一个元素后容量扩充为10,此后当数据填满后默认按照
int newCapacity = oldCapacity + (oldCapacity >> 1);
将容量扩充为原来的1.5倍。例如,添加第11个元素时容器从10扩容为15.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//添加元素
list.add("a");
list.add("b");
list.add("c");
list.add("a");
//移除元素
list.remove("b");
//元素个数
int size = list.size();
//获取指定元素的第一个下标
int f_i = list.indexOf("a");
//获取指定元素的最后一个下标
int l_i = list.lastIndexOf("a");
//根据下标获取元素
String str = list.get(0);
//覆盖指定位置的元素
list.set(0, "m");
}
}
LinkedList
- 基于链表的数据结构
- 查找元素,LinkedList性能不如ArrayList,但对于增删操作,LinkedList更占优势。
Vector
- 基于动态数组的数据结构;
- 线程同步,可用于多线程
- 数据填满时默认扩充为原来的两倍。
Queue
public class Test {
public static void main(String[] args) {
Queue<Integer> q = new LinkedList<>();
//添加元素
q.add(2);
q.add(3);
q.add(4);
//访问队首,不修改队列,无元素返回null
Integer a = q.peek();
System.out.println(a);
//访问队首,然后移除队首元素,无元素返回null
Integer b = q.poll();
System.out.println(b);
System.out.println(q.peek());
//获取长度
int size = q.size();
for(Integer x : q){
System.out.print(x + " ");
}
System.out.println();
System.out.println(q);
}
}
Map
- 键根据哈希值存储
- key和value只能是引用数据类型
HashMap
基本操作
class Test2{
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
//添加元素
map.put("name", "laowang");
map.put("age", "50");
//后添加元素覆盖初始元素
map.put("name", "laoli");
map.put("no", "0");
// map.clear();
//移除元素
map.remove("age");
//判断键是否存在
System.out.println(map.containsKey("name"));
System.out.println(map);
//判断值是否存在
System.out.println(map.containsValue("laoli"));
//由键取值
String value = map.get("name");
//获得键的集合
Set<String> keys = map.keySet();
System.out.println(keys);
//修改对应键的值
map.replace("name", "laowang");
System.out.println(map);
}
}
实现原理
- 数据结构: jdk1.8以前是数组+链表 ,jdk1.8以后是数组+链表+红黑树。
- 当添加一个元素时,首先计算键值的哈希值,以此来确定插入到数组中的位置。如果确定的位置已存在元素,就添加到哈希值相同的元素的后边,于是就形成了链表。当链表长度大于8且数组长度大于64时,链表转化为红黑树,以此提高查找效率。红黑树中的结点个数小于6后,又会转化回链表。
源码解释
- 默认初始容量=16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
- 构造函数中未指定时使用的扩容因子
当一个map中存储的元素个数,超过map容量的75%时,触发扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
- 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;//1073741824
- 树型阈值,当链表长度等于8时,链表转为红黑树
static final int TREEIFY_THRESHOLD = 8;
- 非树型阈值,当红黑树中元素长度等于6时,红黑树转为链表
static final int UNTREEIFY_THRESHOLD = 6;
- 最小树容量,当链表长度大于等于8且map中的元素(k-v)个数超过64时,链表就转换为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;
HashMap的put(key, value)方法实现原理
面试问题
- HashTable, HashMap,TreeMap的区别是什么?
- HashTable线程同步,HashMap非线程同步。
- HashTable不允许<键,值>有空值,HashMap允许<键,值>有空值。
- HashTable使用Enumeration,HashMap使用Iterator。
- HashTable中hash数组的默认大小是11,增加方式的old*2+1,HashMap中hash数组的默认大小是16,增长方式一定是2的指数倍。
- TreeMap能够把它保存的记录根据键排序,默认是按升序排序。
二叉树
二叉搜索树
- 又叫二叉查找数、二叉排序数
- 要求,要么是空树要么满足:
- 若左子树不空,则左子树所有结点的值小于根结点的值
- 若右子树不空,则右子树所有结点的值大于根结点的值
- 左右子树分别为二叉搜索树
- 没有值相等的结点
平衡二叉树(AVL)
- AVL树的名字来源于它的发明作者G.M. Adelson-Velsky 和 E.M. Landis。
- 要求:
- 是一颗二叉搜索树
- 每个结点的左右子树高度差不超过1
- 右旋(左旋同理)
- 双旋转(先左旋再右旋)