为什么需要集合类
- 数组的特点是长度确定,只能存储单一数据类型,初始化后长度不可修改,提供的方法有限且效率不高,无法满足诸如无序,不可重复等复杂要求。
单列集合Collection接口
- List有序可重复集合
- ArrayList
- 底层是数组,支持随机访问,查询效率高,插入和删除效率低,不用存储指针故空间花费比LinkedList低。
- 初始容量为10个元素,扩容时容量乘以1.5倍,将旧数组内容复制到新数组中
- LinkedList
- 底层是双向循环链表,不支持随机访问,查询效率低,插入和删除效率高,每个节点需要存储指针,空间花费比ArrayList高。
- Vector
- 线程安全,通过加synchronized关键字实现,效率低。
- Set无序不可重复集合
- HashSet
- HashSet底层是HashMap,HashSet将数据作为HashMap的key,而HashMap的value使用一个相同的虚值来保存。因为HashMap的key值本身就不允许重复,HashMap遇到重复的kv值会拿新v覆盖掉旧v,而HashSet直接返回插入失败即可。
- LinkedHashSet
- 在Hashset基础上使用双向链表来保存元素添加的前后关系以实现有序
- TreeSet
- 可排序集合(注意可排序是元素大小,LinkedHashSet的有序是插入顺序),底层是红黑树
- Queue队列
- ArrayDeque
- PriorityQueue
- BlockingQueue
- 底层可以由数组或链表实现,队列为空时会阻塞线程获取元素,队列满时会阻塞线程插入元素,队列满足获取或插入条件时会通知被阻塞的线程。
- Collection接口与Collections接口
- Collection是集合的接口
- Collections是操作Collection和Map的工具类,Collections提供synchronized系列方法,可以将指定集合包装成线程同步的集合,可以解决线程安全问题。
Map接口
- HashMap
- LinkedHashMap
- 在HashMap基础上加了双向链表结构,可以记录元素的添加顺序
- TreeMap
- 在HashMap基础上实现了按照元素大小排序功能,底层为红黑树
- ConcurrentHashMap
- 线程安全的HashMap,默认并发度为16,若设置了其他并发度,会使用大于等于该值的最小的2的幂指数作为实际并发度,方便计算。
- 底层实现
- jdk7时由Segment数组结构和HashEntry数组结构组成,即每个Segment控制n个HashEntry,Segment继承了ReentrantLock,为其下的HashEntry加锁,可以并发访问不同的Segment。
- jdk8时底层与HashMap相同(数组+链表+红黑树),抛弃了Segment,采用CAS和synchronized实现更低粒度的锁,锁住的是链表/红黑树的根节点,大大提高了并发度。
- put方法执行逻辑
- jdk7时,先计算出hash值,定位到Segment尝试获取锁,获取失败则自旋获取锁,自旋64次后改为阻塞获取锁。获取到锁后与HashMap的put操作相同。
- jdk8时,先计算出hash值,定位到头结点,若为null,通过cas的方式尝试添加;若头结点正在扩容,则参与一起扩容;否则用synchronized锁住头结点,进行插入。
- get方法
- 不需要加锁,因为Node的值和指针用了volatile修饰,在多线程环境下修改值或指针对其他线程可见,这也是它比其他并发集合效率高的原因。注意这与哈希桶用volatile修饰无关(这是为了数组扩容时保证可见性)
- 不支持value为null
- 因为多线程环境下无法区分是找到了null值还是没有找到key而返回null(例如使用containsKey时有线程插入了null)
- 线程安全
- 只有Hashtable,Vector,Stack,ConcurrentHashMap是线程安全的,但ConcurrentHashMap效率比其他高,因为锁的粒度小,其他都是直接给整个集合加了一把大锁。
其他
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Person other) {
return this.age - other.age;
}
}
public class ComparableExample {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 20));
Collections.sort(people);
}
}
public class ComparatorExample {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 20));
Comparator<Person> ageComparator = Comparator.comparingInt(Person::getAge);
Collections.sort(people, ageComparator);
System.out.println("Sorted by age: " + people);
Comparator<Person> nameComparator = Comparator.comparing(Person::getName);
Collections.sort(people, nameComparator);
System.out.println("Sorted by name: " + people);
}
}