集合框架
- 三大接口:
Iterator
,Collection
,Map
- 工具类:
Collections
Arrays
Java提供的默认排序方法
1.Arrays.sort()
2.Collections.sort()
(底层是调用 Arrays.sort())
1.对于原始数据类型,目前使用的是所谓双轴快速排序(Dual-Pivot QuickSort),是一种改
进的快速排序算法,早期版本是相对传统的快速排序DualPivotQuicksort.java
typically faster than traditional (one-pivot) Quicksort implementations
DualPivotQuicksort
2.对象数据类型 使用TimSort 归并和二分插入排序(binarySort)结合的优化排序算法
思路:
查找数据集中已经排好序的分区(这里叫 run),
然后合并这些分区来达到排序的目的。
Collections.sort->list::sort->Arrays.sort->TimSort.sort
{1, 2, 3, 4, 5, 9, 7, 8, 10, 6}
输出 6{9,8,7,6,5,4, 10}
输出6 并且reverse(0,6)
->[4, 5, 7, 6, 8, 9, 10]
3.java8的parallelSort
ForkJoin框架
package java.util;里的常用类
- Vector,Arraylist,LinkedList implements List
- LinkedList implents Queue
- List,Queue,Set implememts Collection
java9 of静态工厂不可变
Java 9 中,Java 标准类库提供了一系列的静态工厂方法,比如,List.of()、Set.of(),大大简
化了构建小的容器实例的代码量。不可变的,符合我们对线程安全的需求
Vector ArryList LinkedList的区别
同:
1.都实现集合框架中的 List,有序集合。
2.都可以按照位置进行定位、添加或者删除的操作,都提供迭代器以遍历其内容。
不同:
1.Vector在扩容时会提高 1 倍,而 ArrayList 则是增加 50%。
vector:
1 | int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity); |
2.只有Vector是线程安全的。
ArrayList
fianl修饰的变量,JVM也会提前给我们初始化好。???
1 | //变量 |
.add(e)
- 创建Object数组,并拷贝 ->ensureCapacityInternal(size+1)
->ensureExplicitCapacity(size+1);
->grow(size+1)->Arrays.copyOf
->new Object[newLength],System.arraycopy - elementData[size++] = e;
- object的长度为10,size为逻辑长度,不是数组长度,
minCapacity = Math.max(DEFAULT_CAPACITY=10, minCapacity
- 第一次扩容:就一个元素,在堆内存中占了10个位置
- 之后扩容:
int newCapacity = oldCapacity + (oldCapacity >> 1);
//>>=/2
.remove(index)
1.将index+1后的numMoved个元素从index开始复制obj->obj
1 | System.arraycopy(elementData, index+1, elementData, index, |
2.长度-1,最后一个null elementData[--size] = null;
.romove(Object o)
循环查找o==null!=null->fastremove(index)(基本同上)
1 | //当o!=null,按List规范重写equal!! |
Arrays
@SuppressWarnings("unchecked")
编译器消除警告- unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。
- Arrays.
vector
1.ArrayListhe和Vector在用法上完全相同addElement(Object obj)
和add(Object obj)
没什么区别
1 | public synchronized void addElement(E obj) { |
1 | public synchronized boolean add(E e) { |
Vector里有一些功能重复的方法,这些方法中方法名更短的是属于后来新增的方法.更长的是原先vector的方法.而后来ArrayList是作为List的主要实现类.
线程同步不应该使用Vector 应该使用java.util.concurrent.CopyOnWriteArrayList
class Stack<E> extends Vector<E>
Deque 接口及其实现提供了 LIFO 堆栈操作的更完整和更一致的 set
Deque<Integer> stack = new ArrayDeque<Integer>();
LinkedList<E> implements List<E>, Deque<E>,
ArrayDeque循环数组
https://github.com/CarpenterLee/JCFInternals/blob/master/markdown/4-Stack%20and%20Queue.md
head
指向首端第一个有效元素,tail
指向尾端第一个可以插入元素的空位
void addFirst(E e)
1 | elements[head = (head - 1) & (elements.length - 1)] = e;//越界处理 |
1.head前有空位 2.head是0,加到最后,如果最后是tail则扩容:
- elements.length必需是2的指数倍,elements - 1就是二进制低位全1
- 跟head - 1相与之后就起到了【取模】的作用
- 当head-1=-1;相当于对其取相对于elements.length的补码(正数就是本身)
1 | int head = 10; |
addLast
1 | elements[tail] = e; |
void doubleCapacity()
System.arraycopy
1 | native void arraycopy(Object src, //原数组 |
1 | int p = head; |
pollFirst()删除并返回Deque首端(head)元素
pollLast()
int t =(tail-1)&(element.length-1);
E peekFirst()&E peekLast 返回但步删除
LinkedList 双向链表
Queue queue = new LinkedList();
内部静态类Node
Arrays.asList
String可以
1 | String[] ss = {"da","da"}; |
基本数据类型不行
1 | int[] ss = {1,2}; |
源码:基本数据类型
1 | public static <T> List<T> asList(T... a) { |
泛型
泛型方法:
类型参数放在修饰符public static后,返回类型之前
- 元素限定
<T extends AutoCloseable> void closeAll(ArrayList<T> elems)
确保Array的元素类型是AutoCloseable的子类;extends
表示子类型、类型限定
- 多个限定
T extends Runnable & AutoCloseable
,只能有一个限定类,放在第一个,其它都是接口
- Manager是Employee子类但
ArrayList<Manger>
不是ArrayList<Employee>
子类
因为1
2
3ArrayList<Manger> bosses = new ArrayList<>();
ArrayList<Employee> empls = bosses; //非法
empls.add(new Employee(...)); //可以在管理员列里添加普通成员
如果使用数组Manger[]和Employee[] 转型是合法的,但赋值会爆ArrayStoreException
如果不对ArrayList写操作,转换是安全的。可以用子类通配符
<? extends Employee> staff
可以传Arraylist- staff.get(i)可以 可以将
<? extends Employee>
转成Employee
- staff.add(i)不行 不能将任何对象转成
<? extends Employee>
- staff.get(i)可以 可以将
- 父类通配符
<? super Employee>
常用于函数式对象参数(用lambda调用)
泛型类型擦除
1 | List<String> l1 = new ArrayList<String>(); |
1 | List<Integer> l2 = new ArrayList<Integer>(); |
泛型约束
类型变量不能实例化
T[] result = new T[n];
错误- 以【方法引用】方式提供数组的构造函数
Sting[]::new
IntFunction<T[]> constr
T[] result = constr.apply(n)
反射
1
2
3
4
5
6
7public static <T> T[] repeat(int n,T obj,Class<T> c1){
//编译器不知道类型,必须转换
"unchecked") (
T[] result = (T[])java.lang.reflect.Array.newInstance(c1,n);
for(int i=0;i<n;i++)result[i]=obj;
return result;
}调用
String[] greetings = repeat(10,"Hi",String.class);
- 最简单的方法
1
2
3
4
5public static <T> ArrayList<T> repeat(int n,T obj){
ArrayList<T> result = new ArrayList<>();
for(int i =0;i<n;i++) result.add(obj);
return result;
}
- 以【方法引用】方式提供数组的构造函数
泛型数组
Entry<String,Integer>[]
是合法的,但是初始化要@SuppressWarnings(“unchecked”)
正确方法:ArrayList<Entry<String,Integer>>
1 | //可以 |
guava组件
ImmutableList<String> ilist = ImmutableList.of("a","b");
不可变List- 过滤器
- 工具类
Lists
1 | List<String> lit = Lists.newArrayList("aaa","ddd","bbb"); |
1 | Collection<String> cl =Collections2.filter(lit,(e)->e.toUpperCase().startsWith("a")); |
转换 (有问题)日期
1
2Set<Long> set = Sets.newHashSet(20170801L,20980320L,19950730L);
Collection<String> col = Collections2.transform(set,(e)->new SimpleDateFormat("yyyy-MM-dd").format(e));组合函数
Functions.compose(f1,f2)
用google的Function
集合操作
- 交集
SetView<Integer> v1 = Sets.intersection(set, set2);
- 差集:只是1中没有在2中的元素
.difference(s1,s2)
- 并集:把重复的只留一份
Mutiset
无序可重复
1 | //输出[study, dayday, up, good x 2] |
获取次数
1 | Set<String> ele = hms.elementSet(); |
Multimap
Key 可以重复,一个键对应一个Collection
1 | //输出: |
BiMap
双向map 键值都不能重复
BiMap<String,String> map = HashBiMap.create();
key和val可以反转map.inverse()
双键Map 行,列,值
Table<String,String,Integer> table = HashBasedTable.create();
获取条settable.cellSet()
多对多
拆分成两个一对多Student和Course生成StudentAndCourse
1 | class StudentAndCourse{ |
Collections工具类
排序类:针对List(set不用排序)
shuffle(list)
随机打乱reverse(list)
.sort()/(,Comparator c)
.swap
1
2
3public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));}ArrayList: set返回旧值
1
2
3
4
5
6public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}rotate(list,int)
i移动到(i+d)%sizerotate1(list, distance);
: 数组实现,并且长度小于100
if (list instanceofRandomAccess
||list.size()
< ROTATE_THRESHOLD)
leetcode的算法31
2
3reverse(list.subList(0, mid));
reverse(list.subList(mid, size));
reverse(list);
查找
binarySearch
,max
,min
(遍历compare/compareTo)fill(List,o)
填充 (遍历调用set)frequency(c,o)
c中与o相等元素数量(遍历equals)replaceAll(list,old,new)
遍历equals,set
同步重建,加上代码块的锁
只有vector/hashtable比较古老是安全的
synchronizedList(list)
…
设置不可变的集合emptyXXX,singletonXXX,unmodifiableXXX
EmptyList
防空指针UnmodifiableCollection
重写了add、remove等方法,直接抛异常singletonMap
size=1
其它
disjoint(,)
没有相同元素返回trueaddAll(c,T...)
全部加入creverseOrder(Comparator<T> cmp)
返回比较器1
2//反转排序[5, 4, 3, 2, 1]
Collections.sort(list,Collections.reverseOrder());
Iterator
- ListIterator
- Enumeration Vector使用
public Enumeration<E> elements()
1
2
3
4Enumeration<Integer> es = v.elements();
while(es.hasMoreElements()){
System.out.println(es.nextElement());
}
迭代器设计模式
1.迭代器接口 2.迭代器的实现(用构造函数传入(3))3.抽象类 4.具体类(return new迭代器)
forEach jdk1.8
- ArrayList:
public void forEach(Consumer<? super E> action)
Consumer
接口:void accept(T t);
1
2
3
4
5
6default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
Map
- 键值set
Set<Map.Entry<Integer,String>> enry = map.entrySet();
- 值
Collection<String> cl = map.values();
forEach(BiConsumer<? super K, ? super V> action)
- 权限查询
.containsKey()
- 遍历:
1
for(Map.Entry<String,String> entry:map.entrySet()){}
Map接口中的新方法
getOrDefault(Object key, V defaultValue)
不存在返回默认值1
return (((v = get(key)) != null) || containsKey(key))? v: defaultValue;
V putIfAbsent(K key, V value)
不覆盖添加 原本的put覆盖并返回旧值1
2V v = get(key);
if (v == null) {v = put(key, value);}boolean remove(Object key, Object value)
保证key和val都有1
2
3
4
5
6
7Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;boolean replace(K key, V oldValue, V newValue)
V compute
对key位置上的kv对用BiFunction最后更新并返回计算后的值put(key, newValue);
1
2map.compute(1,(k,v)->v+="1");
map.computeIfAbsent(5,(k)->"空空空");merge
1
map.merge(1,"222",(oldv,newv)->oldv+newv);
TreeMap
TreeSet
1 | // Dummy value to associate with an Object in the backing Map |
HashMap 数组+链表+(链表长度达到8,会转化成红黑树)
不是同步的,支持 null 键和值
Ideally, under random hashCodes, the frequency of nodes in bins follows a Poisson distributionwith a parameter of about 0.5 on average for the default resizingthreshold of 0.75,
负载因子0.75的清空下,bin满足泊松分布(exp(-0.5) * pow(0.5, k) /factorial(k)).
落在0的桶的概率有0.6
用树存储冲突hash when bins contain enough nodes to warrant use
TREEIFY_THRESHOLD = 8;
- 计算hash值 【扰动函数】
1
2
3
4static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
实验:
1 | int a = (65535<<2)+1; |
put(K key, V value) {return putVal(hash(key), key
putVal
1
2
3n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)//n=length=16
tab[i] = newNode(hash, key, value, null);putVal如果Node链表太长
1
2if (binCount >= TREEIFY_THRESHOLD - 1) //TREEIFY_THRESHOLD==8
treeifyBin(tab, hash);//变成红黑树
TreeNode<K,V> extends LinkedHashMap.Entry<K,V>
65536(2^16)>>>16 ==1
- table uses power-of-two masking
- 为了用
&2^n-1
取模 2^n的table长不是素数很容易冲突
- *spreads the impact of higher bits downward
- int类型自带hashCode范围-2147483648到2147483648
i = (n - 1) & hash
插入位置 n-1=15是1111低位掩码高位归零- int长32
>>>16
是int的高16位,低16与高16位^
,混合hash的高位低位key1=0ABC0000 & (16-1) = 0
(8个16进制数,共32位)key2=0DEF0000 & (16-1) = 0
hashcode的1位全集中在前16位。key相差很大的pair,却存放在了同一个链表
把hashcode的“1位”变得“松散”,比如,经过hash函数处理后,0ABC0000变为A02188B,0DEF0000变为D2AFC70
Hashtable 数组加链表没用二叉树
数据结构一样的名字不同:
hashtable:private transient Entry<?,?>[] table;
默认大小11
hashmap: transient Node<K,V>[] table;
LinkedHashMap 双重链表
set
- String、Path都有很好的hash函数
- 顺序遍历集合
TreeSet
实现了SortedSet
和NavigableSet
接口 - set的元素必须实现
Comparable
接口或者构造函数中有Comparator
HashSet
HashMap实现,用空对象占位
1
2
3
4
5private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}保持不重复 使用HashMap的 putVal
- 比较hashCode相同不一定是同一个对象,再比较equals
- 重写放入hash的类的hashCode:
LinkedHashSet 用链表记录插入的位置
TreeSet 排序
- 需要实现
Comparatable
或传入Comparator
,会根据compare的值覆盖,去重
NavigableSet
使用元素自然顺序排序,or 用Comparator