集合
也就是容器。理论上: 集合存储不同==(类类型 引用类型)==类型的元素的。 实际开发: 存储相同类型
数组也是容器。存储相同数据类型的元素 length add/delete 操作比较复杂 效率最快 查询(index)
元素特征 | 元素重复 | 元素有序 | |
---|---|---|---|
Collection | 一个元素(单列) | 不定(看实现类) | 无序(没有索引位置的) |
Map | 一组元素(键值对 key—value) | key唯一的 value可重复的 | 无序 |
泛型
标识: <> 泛型类 泛型接口 泛型方法
A-z: 参数化数据类型(代表所有的引用类型)
泛型: 类型的自动转换
与集合结合运用的时候 限制集合元素的数据类型的。
使用集合: 新增(很少做 底层方法) 遍 历(用户)
数据库—> 查询数据库数据 存储集合
1. Collection
常用功能方法:
boolean add(E e) 新增集合元素
boolean remove(Object o)
default boolean removeIf(Predicate<? super E> filter) 删除满足条件的元素
void clear() 清空集合所有元素
boolean isEmpty() 判断集合是否是空
boolean contains(Object o) 判断集合里面是否包含指定的元素
int size()
Iterator<E> iterator() 遍历功能 获得集合对象迭代器对象(将集合的所有的元素 都放在迭代器对象中)
boolean hasNext() 判断光标后面是否有更多的元素需要迭代
E next() 获得光标之后的元素数据
default void remove() 删除光标之后元素
default void forEach(Consumer<? super T> action) 遍历
Object[] toArray() 集合转数组
<T> T[] toArray(T[] a) 推荐使用
参照CollectionDemo.java代码
list vs Set
元素是否有序(索引) | 元素是否重复 | |
---|---|---|
List | 有索引位置 | 数据可重复 |
Set | 无序 | 唯一 不可重复的 |
1.1 List
void add(int index, E element)
E get(int index)
E remove(int index)
E set(int index, E element)
List<E> subList(int fromIndex, int toIndex)
常用实现类:
常用的数据结构: 数组 链表 哈希表 栈 红黑树 队列
链表:
单向: 记忆当前数据 以及下一个元素引用 element next
双向: 记忆当前数据 以及下一个还有上一个元素的引用 prev element next
底层数据结构 | 增加/删除/查询效率 | 线程安全 | |
---|---|---|---|
ArrayList | 动态数组 | 查询最快 | 不安全 |
LinkedList | 双向链表 | 新增 删除最快 查询慢 | 不安全 |
Vector | 动态数组 | 偏慢 | 安全 |
CopyOnWriteArrayList | 动态数组 | 比vector效率偏高 | 安全 |
1. ArrayList
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
为什么查询比较快?
1.底层的数据结构
2.RandomAccess 快速访问
ArrayList() 构造一个初始容量为十的空列表。
ArrayList(int initialCapacity) 推荐使用 10
ArrayList(Collection<? extends E> c)
1.1 add()
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
// {} 初始化为0 10???
//elementData: 维护集合里面所有元素数据
}
// private int size; 维护集合的元素个数
public boolean add(E e) {//100
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {//1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));//10
}
//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);//(10,1)
}
return minCapacity;
}
//protected transient int modCount = 0;
//modCount: 代表操作集合元素次数 add/delete modCount都会累增+1
private void ensureExplicitCapacity(int minCapacity) {//10
modCount++; //modCount = 1
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);//扩容
}
private void grow(int minCapacity) {//10
// 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.2 remove()
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {//删除元素索引
modCount++;//
int numMoved = size - index - 1;
if (numMoved > 0)//判断索引后面是否还存在元素 元素移动位置
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
2. LinkedList
手写一个双向链表。 可以充当队列。FIFO first in first out
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
LinkedList()
void addFirst(E e) /boolean offerFirst(E e) /void push(E e) 新增元素至头部
void addLast(E e) / boolean offer(E e) /boolean offerLast(E e)新增元素至尾部
E getFirst() /E peek() / E peekFirst() /E element() 查询第一个元素
E getLast() /E peekLast() 查询最后一个元素
E poll() /E pop() /E pollFirst()/E remove() /E removeFirst() 查询并删除第一个元素
E pollLast()/E removeLast() 查询并删除最后一个元素
1. add()
public boolean add(E e) {
linkLast(e);
return true;
}
// transient Node<E> last; //null
// transient Node<E> first; //null
void linkLast(E e) {//200
final Node<E> l = last;//null
final Node<E> newNode = new Node<>(l, e, null);//(null,200,null)
last = newNode;//last=200
if (l == null)
first = newNode;//200
else
l.next = newNode;
size++;
modCount++;
}
//第2次新增
void linkLast(E e) {//300
final Node<E> l = last;//第1次的元素 200
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
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;
}
}
linkedList.remove(1);
num = 3 index = 1; size = 5
linkedList.remove(3);
index =3 size = 5
if (index < (size >> 1)) { //(1<2)
Node<E> x = first;//第一个元素 1
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;//7
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
3. Vector
Vector(int initialCapacity)
Vector(int initialCapacity)
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {//1
// overflow-conscious code
if (minCapacity - elementData.length > 0)// 1-10
grow(minCapacity);
}
private void grow(int minCapacity) {//11
// overflow-conscious code
int oldCapacity = elementData.length;//10
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
//如果不指定capacityIncrement 每次都是2倍增长
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
4. CopyOnWriteArrayList
final transient ReentrantLock lock = new ReentrantLock();
public boolean add(E e) {
final ReentrantLock lock = this.lock;//可重入锁
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;//0
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
1.2 Set
元素唯一的 元素无序的
数据结构 | 元素是否可以为null | 线程安全 | |
---|---|---|---|
HashSet | HashMap | 可以 | 否 |
LinkedHashSet | HashMap | 可以 | 否 |
TreeSet | TreeMap | 不可以 | 否 |
CopyOnWriteArraySet | 数组 | 可以 | 是 |
2. Map<K,V>
存储一组元素: key—value
常用实现类
数据结构 | k/v是否可以为null | 线程安全 | |
---|---|---|---|
TreeMap | 红黑树 | k不能为null v可以为null | 否 |
HashMap<K,V> | 位桶(数组)+单向链表+红黑树/哈希算法 | 都是可以为null | 否 |
ConcurrentHashMap | 哈希表 | 都不能为null | 是 |
LinkedHashMap | 链表+哈希表 | 都是可以为null | 否 |
HashTable | 哈希表 | 都不能为null | 是 |
常用方法
static interface Map.Entry<K,V> 将map的k和v封装成一个个的entry对象
1.K getKey()
2.V getValue()
void clear()
boolean containsKey(Object key)
boolean containsValue(Object value)
Set<Map.Entry<K,V>> entrySet() 获得map集合的k/v 装成一个个的entry对象存储在set集合里面
Set<K> keySet() 获得map集合里面所有key
default void forEach(BiConsumer<? super K,? super V> action)
V get(Object key)
default V getOrDefault(Object key, V defaultValue)
boolean isEmpty()
V put(K key, V value) 新增
V remove(Object key)
default boolean remove(Object key, Object value)
default V replace(K key, V value)
default boolean replace(K key, V oldValue, V newValue)
int size()
Collection<V> values() 获得map集合里面所有的value
1. HashMap<K,V>
- 基于哈希表的实现的
Map
接口。 k/V都可以为null 集合数据完全无序。 线程不安全
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
常用构造:
HashMap() 创建map集合对象 默认初始容量(16)和默认负载系数(0.75)。
HashMap(int initialCapacity) 指定初始化容量 推荐
注意:
initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
反例: HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素增加而被迫不断扩容,resize()方法总共会调用 8 次,反复重建哈希表和数据迁移。当放置的集合元素个数达千万级时会影响程序性能。
源码分析:
final float loadFactor;
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);//1 1 100
}
static final int hash(Object key) {//计算key的hash
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
transient Node<K,V>[] table; //数组 位桶 null 存储map集合元素
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;//第一次新增元素的时候执行resize() 扩容
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);//新增新的元素 key不重复的时候
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
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {//hash一致 但是key内容不同 链表存储元素
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
//TREEIFY_THRESHOLD=8
//binCount=7 代表同一个hash下的元素有7个的时候 该新增第8个
//将链表的数据改为树状结构维护数据
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
//在链表上,添加了多个重复的key 需要value覆盖
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)//threshold: 数组是否扩容临界值
resize();
afterNodeInsertion(evict);
return null;
}
//threshold=12
static final int UNTREEIFY_THRESHOLD = 6;//当树状元素个数<6 将树状转为链表结构
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
//第一次新增元素的时候 扩容
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
2. LinkedHashMap
新增顺序遍历顺序是一致的
如果自定义类对象充当HashMap或者LinkedHashMap的key的话,一定要重写equals和hashcode 保证元素的唯一性
private static void testHashMap1() {
//如果自定义类对象作为map的key存储 一定要重写hashcode和equals
HashMap<User,Integer> map = new HashMap<>(16);
map.put(new User(1,"jim",123),1);
map.put(new User(1,"jim",123),2);
map.put(new User(1,"jim",123),3);
map.put(new User(1,"jim",123),4);
map.put(new User(1,"jim",123),5);
map.forEach((user,value)->{
System.out.println(user+","+value);
});
}
@Setter
@Getter
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class User {
private Integer id;
private String name;
private Integer age;
}
3. TreeMap
元素有序: key 会按照自然顺序排列。 默认是升序。 要求Key不能为null key的数据类型必须实现Comparable接口。
TreeMap(Comparator<? super K> comparator) 使用自定义Comparator规则排序
TreeMap() 默认使用Comparable的规则
//private transient Entry<K,V> root; null 第一次新增的那个元素
public V put(K key, V value) {//1 100
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
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);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(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;
}
自定义的类对象充当TreeMap的key 有可能会出现类型转换的异常:
Exception in thread “main” java.lang.ClassCastException: com.javasm.map_.User cannot be cast to java.lang.Comparable
private static void testTreeMap1() {
TreeMap<User,Integer> map = new TreeMap<>();
//Exception in thread "main" java.lang.ClassCastException: com.javasm.map_.User cannot be cast to java.lang.Comparable
map.put(new User(1,"jim",123),1);
map.put(new User(1,"jim",123),2);
map.put(new User(1,"jim",123),3);
map.put(new User(1,"jim",123),4);
map.put(new User(1,"jim",123),5);
map.forEach((user,value)->{
System.out.println(user+","+value);
});
}
3. 集合元素排序
3.1 List
private static void demo1() {
// List<Integer> list = new ArrayList<>(10);
// Collections.addAll(list, 100, 10, 90, 1, 200, 100);
//Arrays.asList()
List<Integer> list = Arrays.asList(100, 10, 90, 1, 200, 100);
System.out.println("排序之前:" + list);
Collections.sort(list);//Integer.compareTo()
System.out.println("排序之后:" + list);
System.out.println(list.get(0));
System.out.println(list.get(list.size() - 1));
}
private static void demo2() {
List<User> list = Arrays.asList(
new User(1, "jim", 20),
new User(10, "tim", 50),
new User(11, "jim", 41),
new User(6, "jim", 41));
//默认按照年龄升序排列 如果年龄相同 按照id降序排列
System.out.println("排序之前:" + list);
Collections.sort(list);
System.out.println("排序之后:" + list);
}
private static void demo2() {
List<User> list = Arrays.asList(
new User(1, "jim", 20),
new User(10, "tim", 50),
new User(11, "jim", 41),
new User(6, "jim", 41));
//排序规则: 默认按照年龄升序排列 如果年龄相同 按照id降序排列
//一般会使用外部比较器: java.util.Comparator<T>
System.out.println("排序之前:" + list);
// Collections.sort(list, new Comparator<User>() {
// @Override
// public int compare(User user1, User user2) {
// int result = user1.getAge().compareTo(user2.getAge());
// if (result == 0) {
// result = user1.getId().compareTo(user2.getId());
// }
// return result;
// }
// });
/* Collections.sort(list, (user1, user2) -> {
int result = user1.getAge().compareTo(user2.getAge());
if (result == 0) {
result = user1.getId().compareTo(user2.getId());
}
return result;
});*/
Collections.sort(list, Comparator.comparing(User::getAge).thenComparing(User::getId));
System.out.println("排序之后:" + list);
}
3.2 Set
HashSet / LinkedHashSet 不能直接排
TreeSet 元素按照自然顺序排列
private static void demo2() {
TreeSet<User> set = new TreeSet<>(Comparator.comparing(User::getAge));
Collections.addAll(set,
new User(1, "jim", 20),
new User(10, "tim", 50),
new User(11, "jim", 41),
new User(7, "jim2", 41));
System.out.println(set);
}
3.3 Map
private static void demo3() {
TreeMap<User, Integer> map = new TreeMap<>(Comparator.comparing(User::getAge));
map.put(new User(1, "jim", 20), 1);
map.put(new User(10, "tim", 50), 2);
map.put(new User(11, "jim", 41), 3);
map.put(new User(7, "jim2", 41), 4);
System.out.println(map);
}
4. Collections
提供了很多静态的方法 操作集合元素
static <T> boolean addAll(Collection<? super T> c, T... elements) 将数据新增到指定集合对象里面
void sort(List<T> list) 排序
static <T> void sort(List<T> list, Comparator<? super T> c)
static <T extends Object & Comparable<? super T>>
T max(Collection<? extends T> coll)
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
在集合里面,很多集合类都是现成不安全的类,可以使用Collections里面的方法将现成不安全的集合类转换成现成安全的集合对象。
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)
static void shuffle(List<?> list) 使用指定的随机源打乱集合元素
5. Stream机制
https://developer.ibm.com/zh/articles/j-lo-java8streamapi/
参照: StreamDemo.java