集合
数组。 存储相同数据类型的数据, 可以基本+引用。 容器: 存储(新增)+查看 底层数据结构。
优点:
查询速度最快, index
修改
弊端:
新增元素 数组大小是一定的 手动扩容 Arrays.copyOf()
删除元素 元素数据都是连贯 手动移动位置
集合。 容器。 自动扩容,移动位置。 理论上集合可以存储不同引用数据类型的数据。Object
真实使用上,存储相同类型的数据。 (泛型)
1. Collection
存储单列元素。没有索引位置
泛型的标识: <> A-Z: 参数化类型 (可以是任意的引用数据类型)
public interface Collection<E> extends Iterable<E>
public interface Iterable<T>: 遍历集合元素
Iterator<T> iterator() 获得集合对象的迭代器对象(集合的所有的元素都在Iterator里面)
Iterator:
boolean hasNext() : 判断光标之后是否有更多的元素需要遍历
E next() : 获得光标之后元素数据
default void remove() : 删除元素
default void forEach(Consumer<? super T> action) 使用lamda遍历集合所有的元素
boolean add(E e) 新增一个元素
void clear()
boolean contains(Object o)
boolean isEmpty()
boolean remove(Object o) 删除指定元素
int size()
Object[] toArray()
<T> T[] toArray(T[] a)
default Stream<E> parallelStream()
default Stream<E> stream() 对集合元素执行操作
default boolean removeIf(Predicate<? super E> filter) 删除多个满足条件元素
遍历方式
//遍历: 循环遍历集合元素 1.1. 增强for循环
// for (Object obj : collection) {
if (Objects.equals(100, obj)) {
collection.remove(obj);
}
// System.out.println(obj);
// }
// 1.2 使用Iterator执行遍历(可以遍历期间删除)
/* Iterator iterator = collection.iterator();
//获得集合对象的的迭代器对象 集合的所有元素都在iterator
//判断光标之后有没有元素需要遍历
while (iterator.hasNext()) {
Object element = iterator.next();
// System.out.println(element);
if(Objects.equals(element,100)){
// collection.remove(element);
iterator.remove();
}
}*/
//1.3 forEach方法
// collection.forEach(new Consumer() {
// @Override
// public void accept(Object obj) {
// //obj 就是集合所有的元素了
// System.out.println(obj);
// }
// });
// collection.forEach(obj->{
// System.out.println(obj);
// });
//单纯测试:遍历
// collection.forEach(System.out::println);
1.1 List
元素是否重复 | 元素是否有索引位置 | |
---|---|---|
List | 可以 | 都有索引 |
Set | 唯一 | 没有索引 |
void add(int index, E element) 指定索引位置
E get(int index)
E remove(int index)
ListIterator<E> listIterator()
E set(int index, E element)
List<E> subList(int fromIndex, int toIndex)
集合: 存储 (数据从哪里来? 从数据库里面查询的) 遍历
底层数据结构 | 功能效率 | 线程安全 | |
---|---|---|---|
CopyOnWriteArrayList | 动态数组 | 都慢 | 线程安全 |
ArrayList | 动态数组 | 查询 | 不安全 |
LinkedList | 双向链表 | 新增 删除 | 不安全 |
vector | 动态数组 | 都慢 | 线程安全 |
1.1.1ArrayList
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
RandomAccess: 标识接口 可以支持快速访问。普通for循环比迭代器的效率还要快。
ArrayList() 构造一个初始容量为十的空数组。
ArrayList(int initialCapacity)
ArrayList(Collection<? extends E> c)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
transient Object[] elementData; // non-private to simplify nested class access
//集合的元素数据都在: elementData
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private int size; //统计集合元素个数
public boolean add(E e) {
ensureCapacityInternal(size + 1); //判断底层数组空间容量是否足够
elementData[size++] = e;// elementData[0] = e
return true;
}
private void ensureCapacityInternal(int minCapacity) {//1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static final int DEFAULT_CAPACITY = 10;//初始化数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {//{} 1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {//10
modCount++;// 新增 删除 维护操作集合元素的次数
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);//扩容
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//0
int newCapacity = oldCapacity + (oldCapacity >> 1);//0 1.5倍增长
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;//10
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);// 扩容
}
1.1.2 LinkedList
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
链表:
1.单向链表
当前数据会记得下一个数据的引用 next 当前数据 data
2.双向链表
当前数据会记得下一个数据的引用 next 还会记得上一个数据previous的引用 当前数据 item
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rc48wAll-1605082245767)(pic\LinkedList .png)]
栈: filo 先进后出
队列: FIFO 先进先出
LinkedList()
transient Node<E> last;//null
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);//(null,100,null)
last = newNode;//last = 100的引用
if (l == null)
first = newNode;// transient Node<E> first; = (null,100,null)
else
l.next = newNode;
size++;
modCount++;
}
//双向链表
private static class Node<E> {
E item;//当前元素数据
Node<E> next;//下一个引用
Node<E> prev;//上一个引用
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {// 将整个LinkedList的数据 划分成左右2个部分 进行寻找
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
1.1.3 CopyOnWriteArrayList
线程安全,
1.2 Set
元素唯一的 不可重复的 元素没有索引位置的。
数据结构 | 元素是否可以为null | 线程安全 | |
---|---|---|---|
HashSet | HashMap维护 | 可以 | 否 |
LinkedHashSet | 哈希表+链表LinkedHashMap | 可以 | 否 |
TreeSet | 红黑树 TreeMap | 不可以 | 否 |
CopyOnWriteArraySet | 可以 | 是 |
1.2.1 HashSet
HashSet(int initialCapacity)
1.2.2 LinkedHashSet
1.2.3 TreeSet
2. Map<K,V>
存储的一组元素。键值对 key—value key: 元素都是唯一的 value: 是可以重复的
static interface Map.Entry<K,V> 内部接口 代表map集合里面每组元素数据 称为键值对
boolean isEmpty()
V put(K key, V value) 新增map集合元素
V get(Object key) 根据key获得value
default V replace(K key, V value) 修改指定的key所对应的value
default boolean replace(K key, V oldValue, V newValue)
V remove(Object key) 删除指定key
default boolean remove(Object key, Object value)
int size()
void clear()
boolean containsKey(Object key)
boolean containsValue(Object value)
Set<Map.Entry<K,V>> entrySet() 获得map集合里面所有的键值对
default void forEach(BiConsumer<? super K,? super V> action)
Set<K> keySet() 获得map所有的key
Collection<V> values() 获得map所有的value
常用实现类以及区别
底层数据结构 | K,V是否可以为null | 线程安全 | |
---|---|---|---|
HashMap<K,V> | 数组(位桶)+链表+红黑树 | KV都可以null | 否 |
LinkedHashMap<K,V> | 哈希表+链表 | KV都可以null | 否 |
TreeMap<K,V> | 红黑树 | K不能为null V可以 | 否 |
HashTable<K,V> | 哈希表 | KV都不可以null | 是 同步 |
ConcurrentHashMap<K,V> | 哈希表 | KV都不可以null | 是 CAS: Redis 查询多 |
2.1 HashMap<K,V>
HashMap(int initialCapacity) initialCapacity= 存储元素个数/负载因子+1
HashMap() 默认初始容量(16)和默认负载系数(0.75)。
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
public V put(K key, V value) {// 1 100
return putVal(hash(key), key, value, false, true);
}//putVal(1,1,100,flase,true)
//计算map的key的hash (更加具有随机性 避免hash碰撞)
static final int hash(Object key) {//1
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
transient Node<K,V>[] table;//位桶 数组 存储map集合元素 null
int threshold;//12 扩容的临界点
TREEIFY_THRESHOLD= 8 //链表结构转换成红黑树数据结构数值判断 8
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;//第一次初始化table
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);//存储第一个元素
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;// hash冲突 key值一样 新的value替换旧value
//如果仅仅hash一致 key值不等 比如 Ma NB 会执行下面2个if
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//如果节点为TreeNode 按照红黑树规则进行插入
else {
//按照链表执行操作
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//将新的元素放到上一个元素next属性为null的位置
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
// TREEIFY_THRESHOLD 链表转换红黑树的临界点 7
//可以避免在极端情况下 链表死循环的问题
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
//key值一样 key的hash一致 替换旧value
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
2.1 LinkedHashMap<k,V>
插入元素的顺序与遍历的顺序完全一致。
2.2 TreeMap<k,V>
key: 有序 会按照自然顺序进行排列。 提供排序规则。 与比较器有关系: Comparable Comparator
要求key的数据类型必须实现Comparable
public TreeMap() {
comparator = null;
}
public V put(K key, V value) {
Entry<K,V> t = root;// root = null 始终代表第一个元素
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);// 第一次新增元素 充当root
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
//当我们自定义比较规则的时候 使用Comparator的compare进行比较
if (cpr != null) {
do {//执行逻辑与下面Comparable类似
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
//调用key的数据类型重写的compareTo执行比较
if (cmp < 0)//当前元素小 获得父节点的左边分支 再循环比较
t = t.left;
else if (cmp > 0)//当前元素大 获得父节点的右边分支 再循环比较
t = t.right;
else
return t.setValue(value);//key值一样 直接新的value替换旧的value数据
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;//定位 放到父节点的左边还是右边
else
parent.right = e;
fixAfterInsertion(e);//修正树
size++;
modCount++;
return null;
}
2.3 HashTable<K,V>
Hashtable()
构造一个新的,空的散列表,默认初始容量(11)和负载因子(0.75)。
Hashtable(int initialCapacity)
构造一个新的,空的哈希表,具有指定的初始容量和默认负载因子(0.75)。
2.4 ConcurrentHashMap<K,V>
public class ConcurrentHashMap<K,V>
extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable
ConcurrentHashMap()
创建一个新的,空的地图与默认的初始表大小(16)。
ConcurrentHashMap(int initialCapacity)
创建一个新的空的地图,其初始表格大小适应指定数量的元素,而不需要动态调整大小。
3. 排序
对集合元素排序。
1. 对List集合元素进行排序
public class User implements Comparable<User> {
private Integer id;
private String name;
private Integer age;
//排序规则
@Override
public int compareTo(User user) {
//按照用户年龄升序排列
//年龄相同 按照id降序排列
Objects.requireNonNull(user);
int result = user.age.compareTo(this.age);
if(result==0){
result = user.id.compareTo(this.id);
}
return result;
}
}
Collections.sort(userList, (user1, user2) -> {
int result = user1.getAge().compareTo(user2.getAge());
if (result == 0) {
result = user2.getId().compareTo(user1.getId());
}
return result;
});
Set集合元素排序 TreeSet
如果自定义类型的对象存储到Set集合里面 一定要去重写equals 和 hashcode
为什么要重写类型equals 和 hashcode? 规则
Map map集合的key元素进行排序 TreeMap
如果自定义类型的对象存储到 map集合的key元素 一定要去重写equals 和 hashcode
3.1 Collections
操作集合元素的工具类 Collections
static <T> boolean addAll(Collection<? super T> c, T... elements)
将指定元素数据放入集合中 List/Set
static <T extends Comparable<? super T>> void sort(List<T> list)
static <T> void sort(List<T> list, Comparator<? super T> c)
T max(Collection<? extends T> coll) 获得元素最值 按照自然顺序排列
T max(Collection<? extends T> coll, Comparator<? super T> comp)
T min(Collection<? extends T> coll)
T min(Collection<? extends T> coll, Comparator<? super T> comp)
static void shuffle(List<?> list) 洗牌 打乱集合元素顺序
static <T> List<T> synchronizedList(List<T> list) 将线程不安全的集合对象转换成安全的集合对象
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
static <T> Set<T> synchronizedSet(Set<T> s)
4. Stream
//stream() 流机制 提供了很多对集合元素操作的方法 过滤 聚合 截断
//遍历list集合的时候 执行删除 并发修改的异常 属于迭代器的 fail--fast 快速失败的机制 modCount++
//再1.8- 集合操作 不支持并行化 并行 vs 并发 再同一个时间 执行多个任务
//1.获得流
// Stream.of(10, 100, 100, 0, 3)
// Arrays.stream()
Stream<Integer> stream = hashSet.parallelStream();//集合的元素都在stream里面
//2.操作流
Optional<Integer> optional = stream.min((num1,num2)->{
return num1.compareTo(num2);
});
5. 集合嵌套
//维护省市级联的数据,通过一个省份获得所有的城市的信息
//维护省市级联的数据,通过一个省份获得所有的城市的信息 根据key获得value
// List<Map<String,Object>>
// 一个省份 ---> 多个城市 List Set
Map<String, List<City>> map = new HashMap<>(16);
List<City> cityList = new ArrayList<>(10);
Collections.addAll(cityList,
new City(1, "郑州市1"),
new City(1, "郑州市2"),
new City(1, "郑州市3"),
new City(1, "郑州市4"));
map.put("he", cityList);
cityList = new ArrayList<>(10);
// cityList.clear();//清空
Collections.addAll(cityList,
new City(2, "海淀区1"),
new City(3, "海淀区1"),
new City(4, "海淀区1"),
new City(5, "海淀区1"));
map.put("bj", cityList);
//获得河南省份下的所有的城市
List<City> cityList1 = map.get("he");
System.out.println(cityList1);
List Set
Map<String, List> map = new HashMap<>(16);
List cityList = new ArrayList<>(10);
Collections.addAll(cityList,
new City(1, “郑州市1”),
new City(1, “郑州市2”),
new City(1, “郑州市3”),
new City(1, “郑州市4”));
map.put(“he”, cityList);
cityList = new ArrayList<>(10);
// cityList.clear();//清空
Collections.addAll(cityList,
new City(2, “海淀区1”),
new City(3, “海淀区1”),
new City(4, “海淀区1”),
new City(5, “海淀区1”));
map.put(“bj”, cityList);
//获得河南省份下的所有的城市
List<City> cityList1 = map.get("he");
System.out.println(cityList1);