集合架构
Collection(接口):
所有单例集合的父类Collection集合的功能所有单例集合都可以直接使用;
API:
- public boolean add(E e): 把给定的对象添加到当前集合中 。
- public void clear() :清空集合中所有的元素。
- public boolean remove(E e): 把给定的对象在当前集合中删除。
- public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
- public boolean isEmpty(): 判断当前集合是否为空。
- public int size(): 返回集合中元素的个数。
- public Object[] toArray(): 把集合中的元素,存储到数组中
遍历方式:
1.迭代器
- public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素
- E next():获取下一个元素值!
- boolean hasNext():判断是否有下一个元素,有返回true ,反之false。
Iterator<String> it = lists.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
2.foreach:for(被遍历集合或者数组中元素的类型 变量名称 : 被遍历集合或者数组){}
for(String s: lists){
System.out.println(s);
}
3.lambda表达式: lambda表达式是JKD1.8的新特性,主要是为了简化代码,简化代码如下:
Collection<String> lists = new ArrayList<>();
lists.add("五六七");
lists.add("梅花十三");
lists.add("鸡大宝");
lists.add("小飞");
lists.forEach(s -> {
System.out.println(s);
});
Set(接口):
特点:添加的元素是无序,不重复,无索引的。
HashSet:
- null值:可以存放null值
- 线程:线程不安全
- 底层:哈希表
-
- 其他特定:无序,不重复,无索引
- HashSet的层使用的是HashMap
- JDK1.8以前,哈希表是由数组+链表构成,JDK1.8以后是由数组+链表+红黑树构成
- 当链表的大于8个以后,链表结构就会转成红黑树结构进行存储
- 哈希算法:
- 获取元素对象的哈希值
- 让当前对象的哈希值对都城数组长度取余(数组长度默认是16)
- 取余的结构作为该对线下元素在地城数组的索引位置
- 吧对象元素存入到该索引位置
HashSet研究的两个问题:
1)Set集合添加的元素是不重复的,是如何去重复的?
对于基本数据类型和字符串,Set集合可以直接判断去重
对于引用数据类型,Set集合会按照如下顺序进行判断:
- 调用自己的hashCode方法获取哈希值进行比较,如果返回false直接认为两个对象不重复
- 如果哈希值相同,再调用equals方法比较,返回false表示不重复,true表示重复
所以我们比较两个对象的内容是否相同需要重写hashCode方法和equals方法
2)Set集合元素无序的原因是什么
Set无序的根本原因就是底层使用了哈希表,通过哈希算法觉得存放的位置,所以是无需的。
LinkedHashSet:
- null值:允许 null 元素 但最多只能加一个
- 线程:不安全
- 底层:哈希表,每个元素都带上一个链
- 其他特点:有序,不重复,无索引
TreeSet:
- null值:默认不允许null值,可以通过设置Comparator接口的实例,来实现元素允许为null值
- 线程:不安全
- 底层:红黑树
- 其他特点:无序,不重复,无索引,但是可以自动排序
TreeSet的排序:
- 对于有值特性的元素直接可以升序排序。(浮点型,整型)
- 字符串类型的元素会按照首字符的编号排序。
- 但是对于自定义的引用数据类型,TreeSet默认无法排序,执行的时候直接报错
- 要对引用数据类型进行排序有如下两种方案
- 为对象实现比较器规则接口:Comparable,重写比较方法
public class Student implements Comparable<Student>{
private String name;
private int age;
private String sex;
/**
* 如果程序员认为比较者大于被比较者 返回正数!
* 如果程序员认为比较者小于被比较者 返回负数!
* 如果程序员认为比较者等于被比较者 返回0!
*/
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
//构造方法和getter,setter,tostring方法省略
}
- 直接为集合设置比较器Comparator对象,重写比较方法
public static void main(String[] args) {
TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
}
});
Collections.addAll(students,new Student("五六七",18,"男"),
new Student("梅花十三",17,"女"),
new Student("鸡大宝",20,"公"));
System.out.println(students);
}
List(接口):
特点:添加的元素是有序,可重复,有索引。
ArrayList:
- null值:允许为null值
- 线程:不安全
- 底层:数组
- 查询:快
- 增删:慢
遍历方式
- for循环:因为有索引所以多了个根据索引遍历的方式:
for(int i = 0 ; i < lists.size() ; i++ ) {
String ele = lists.get(i);
System.out.println(ele);
}
- 迭代器:略
- foreach:略
- lambda表达式:略
LinkedList:
- null值:允许为null值
- 线程:不安全
- 底层:链表
- 查询:慢
- 增删:快
遍历方式:同ArrayList(略)
LinkedList和ArrayList的区别
主要是底层实现不同:
ArrayList底层是数组,在内存中是一组连续的代码块,通过索引*单位长度(根据所传泛型觉得单位长度)来寻找元素,所以查询速度很快,但是增删元素就相对较慢;
LinkedList底层是双向链表,在内存中的位置是随机的,链表每个元素都记录了元素的值和下一个元素的地址,想要找到某个元素,只能通过头或者尾元素的地址值查找下一个元素,直到找到要找的元素。
Vector:(淘汰)
- null值:允许为null值
- 线程:安全
- 底层:数组
- 查询:快
- 增删:慢
Vector因为是线程安全的,性能较差,所以几乎淘汰不用了
遍历方式:同ArrayList(略)
总结:
- 如果希望元素可以重复,又有索引,查询要快用ArrayList集合。
- 如果希望元素可以重复,又有索引,增删要快要用LinkedList集合。(适合查询元素比较少,经常要首尾操作元素的情况)
- 如果希望增删改查都很快,但是元素不重复以及无序无索引,那么用HashSet集合。
- 如果希望增删改查都很快且有序,但是元素不重复以及无索引,那么用LinkedHashSet集合。
Map(接口):
Map接口是所有键值对集合的父类
Map集合的特点:
- Map集合的特点都是由键决定的。
- Map集合的键是无序,不重复的,无索引的。
Map集合后面重复的键对应的元素会覆盖前面的整个元素! - Map集合的值无要求。
- Map集合的键值对都可以为null。
- HashMap、LinkedHashMap和TreeMap底层和HashSet、LinkedHashSet、TreeSet的底层是一样的
super点进去
HashMap:
- null值:允许键值都为null值
- 线程:不安全
- 底层:哈希表
- 其他特点:无序不重复
遍历方式:
- “键找值”的方式遍历:先获取Map集合全部的键,再根据遍历键找值。
public static void main(String[] args) {
Map<String,Integer> maps = new HashMap<>();
maps.put("aaa",1);
maps.put("bbb",2);
maps.put("ccc",2);
Set<String> keys = maps.keySet();
for (String key : keys) {
Integer value = maps.get(key);
System.out.println(key + "=" + value);
}
}
- “键值对”的方式遍历:
public static void main(String[] args) {
Map<String,Integer> maps = new HashMap<>();
maps.put("aaa",1);
maps.put("bbb",2);
maps.put("ccc",2);
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
- Lambda表达式。
public static void main(String[] args) {
Map<String,Integer> maps = new HashMap<>();
maps.put("aaa",1);
maps.put("bbb",2);
maps.put("ccc",2);
maps.forEach((k , v) -> System.out.println(k+"="+v));
}
常用API:
- 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集合)。public boolean containKey(Object key)
:判断该集合中是否有此键。
LinkedHashMap:
- null值:允许键值都为null值
- 线程:安全
- 底层:哈希表加上链表结构
- 其他特点:有序不重复
HashTable:
- null值:不允许为null值
- 线程:安全,高并发竞争越激烈性能越差
- 底层:红黑树
- 其他特点:无序不重复
TreeMap:
- null值:都允许
- 线程:不安全
- 底层:红黑树
- 其他特点:无序不重复
IdentityHashMap:
- null值:都允许
- 线程:不安全
- 底层:Hash表来实现Map接口
- 其他特点:无序不重复
ConcurrentHashMap:
- null值:不允许为null值
- 线程:安全,使用分段锁,性能得到优化,高并发下建议使用
- 底层:数组+链表+红黑树
- 其他特点:无序不重复
concurrentHashMap底层对哈希表加锁是只加其中一个桶,所以不影响其他桶进行读写等操作,综合性能高,而HashTable是对一整个哈希表加锁,所以综合性能比较差,已经淘汰了
Properties:
- null值:同Hashtable
- 线程:安全
- 底层:同Hashtable
- 其他特点:无序不重复,集合里只能存字符串类型,是唯一一个与IO流结合的集合
Properties底层是继承了HashTable,这个集合主要是用于读写属性文件,在各框架底层中大量使用。
集合工具类
Collections工具类:
- public static boolean addAll(Collection<? super T> c, T… elements)
给集合对象批量添加元素! - public static void shuffle(List<?> list) :打乱集合顺序。
- public static void sort(List list):将集合中元素按照默认规则排序。
- public static void sort(List list,Comparator<? super T> ):将集合中元素按照指定规则排序。