文章目录
7、 Map 接口
public interface Map<K,V>
将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值,具体实现:HashMap TreeMap Hashtable
void clear() | 清空Map集合中的内容 |
---|---|
boolean containsKey(Object key) | 判断集合中是否存在指定的Key |
boolean containsValue(Object value) | 判断集合中是否存在指定的value |
Set<Map.Entry<K,V>> entrySet() | 将Map接口变为Set集合 |
V get(Object key) | 根据Key 找到其对应的value |
- 键值对存储一组对象
- Key不能重复(唯一),Value可以重复
HashMap
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V> , Cloneable, Serializable
基于哈希表的Map接口的实现。此实现提供所有可选的映射操作,并允许使用null 值和null 键。 (除了非同步和允许使用null之外,HashMap 类与Hashtable大致相同)此类不保证映射的顺序,特别是它不保证该顺序恒久不变
private static void hashMap() {
Map<Integer, String> map = new HashMap<>();
map.put(1, "Tom");
map.put(2, "Jack");
map.put(3, "Vince");
map.put(4, "Bin");
map.put(5, "Lily");
System.out.println("size = " + map.size());
System.out.println(map.get(1));
// 遍历,一次性取出键和值
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
for (Map.Entry e : entrySet) {
System.out.println(e.getKey() + "->" + e.getValue());
}
// 先取出键
Set<Integer> keys = map.keySet();
for (Integer i : keys) {
String value = map.get(i);
System.out.println(i + "->" + value);
}
// 只取出值
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
// forEach
map.forEach((Key, value) -> System.out.println(Key + "->" + value));
}
HashMap 实现原理
- 基于哈希表(数组+链表+红黑树) 1.8JDK
- 初始大小为16
- 把Key对象通过hash() 方法计算hash值,然后用这个hash 值对数组长度取余数(默认16),来决定该对Key对象在数组中存储的位置,当这个位置有多个对象时,以链表结构存储,JDK1.8后,当链表长度大于8时,链表将转换为红黑树结构存储。这样的目的: 为了取值更快,存储的数据量越大,性能表现越明显
- 当数组的容量超过75%,那么表示该数组需要扩充,当前数组容量<<1,扩大1倍,扩充次数过多,会影响性能,每次扩充表示哈希表重新散列(重新计算每个对象的存储位置),我们在开发中尽量要减少扩充次数带来的性能问题。
- 线程不安全
default load factor( 0.75):
增加
每次新建一个HashMap,都会初始化一个table 数组,table数组的元素为Entry 节点Entry 是HashMap 的内部类,包含了Key value next hash,正是由于Entry构成了table 数组的项为链表
put方法:当调用put存值时,HashMap 首先会获取key的哈希值,通过哈希值快速找到某个存放位置,这个位置可以被称之为bucketIndex,hashCode可能存在冲突的情况,发生了碰撞,当碰撞发生时,计算出的bucketIndex也是相同的,这时会取到bucketIndex位置已存储的元素,equals方法就是哈希码碰撞才会执行的方法,HashMap通过hashCode和equals最终判断出k是否存在,如果已存在,则使用新V值特换旧V值,并返回就V值,如果不存在,就放新的键值对。
KeySet() 方法可以获取包含key的set集合,调用该集合的迭代器可以对key值进行遍历,KeySet 是HashMap的内部类,继承(abstractSet, )
EntrySet 是HashMap的内部类,继承AbstractSet,显然entrySet() 遍历的效率比keySet() 高,因为keySet获取key的集合后,要需要调用get方法,相当于遍历了两次
Hashtable
public class Hashtable<K,V> extends Dictionary<K,V>
implements Map<K,V>, Cloneable, Serializable
此类实现一个哈希表,该哈希表将键映射到相应的值。任何非null 对象都可以用作键或值。为了成功在哈希表中存储和获取对象,用作键的对象必须实现hashCode 方法和equals方法。
从JDK1.0开始,数组初始大小为11,加载因子0.75,基于哈希表实现(数组+链表),扩充方法:原数组大小乘2加1
public static void hashtable(){
Map<String,String> table = new Hashtable<>();
table.put("one","Lily");
table.put("two","Tom");
table.put("three","Bin");
table.forEach((key,value) -> System.out.println(key+"->"+value));
}
LinkedHashMap
public class LinkedHashMap<K,V>
extends HashMap<K,V> implements Map<K,V>
Map 接口的哈希表和链表列表实现,具有可预知的迭代顺序。此实现与HashMap不同在于,后者维护着一个运行于所有条目的双重链接列表。依靠着双向链表保证了迭代顺序是插入的顺序
public static void linkedHashMap(){
Map<String,String> table = new LinkedHashMap<>();
table.put("one","Lily");
table.put("two","Tom");
table.put("three","Bin");
table.forEach((key,value) -> System.out.println(key+"->"+value));
}
TreeMap
public class TreeMap<K,V> extends AbstractMap<K,V>
implements NavigableMap<K.V>, Cloneable, Serializable
基于红黑树的NavigableMap实现,该映射根据其键的自然顺序进行排序,或根据创建映射时提供的Comparator进行排序,取决于使用的构造方法,要实现Comparable 或者传入Comparator 类, 键不能重复。如果put中,key 相同,那么值会被替换。
JDK1.8 新加入的方法
getOrDefault: get 方法默认如果key没有找到,那么会得到null
putIfAbsent :map 的put方法,如果key 已经存在了,value 会被覆盖,(源码,先get,如果value是null,在put)
remvoe(Object key, Object value); 键和值都匹配才会被删除
replace(Key, oldvalue, newvalue)
map.compute(1,(k,v)->v+"1");
map.computeIfAbsent(6,(val)->val+"test");
map.merge(1,"888",(old,newv)->old.concat(newv));
Comparatable
Comparable 对实现它的每个类的对象进行整体排序,这个接口需要类本身去实现,实现了Comparable, 可以使用Arrays.sort, 实现Comparable 接口的类的对象,可以用作treeMap 和TreeSet 中的元素
public static void sort(List list,Comparator<? super T> c) 采用了内部类的实现方式,实现compare方法,对list 进行排序。
最后,Comparable 是排序接口,若一个类实现了Comparable 接口,意味着该类支持排序,Comparable 相当于内部比较器,而Comparator 相当于外部比较器
Collections 工具类
排序操作
reverse(List list) 反转指定List 集合中的元素
shuffle(List list) 对List 中的元素进行随机排序,在实现一个碰运气的游戏中有用
sort(List list) 对List 里的元素根据自然升序排序
sort(List list, Comparator c) 自定义比较器进行排序
swap(List list, int i, int j) 指定List 集合中i处元素和j处元素交换
rotate(List list, int distance) 将所有元素向右移位指定长度,如果distance等于size那么结果不变
查找和替换
binarySearch(List list, Object key) | 使用二分搜索法,以获得指定对象在list 中的索引,前提是集合已经排序 |
---|---|
max(Collection coll) | 返回最大元素 |
max(Collection coll, Comparator comp) | 根据自定义比较器,返回最大元素 |
min(Collection coll) | 返回最小元素 |
min(Collection coll, Comparator comp) | 根据自定义比较器,返回最小元素 |
fill(List list, Object o) | 使用指定对象填充 |
frequency(Collection Object, o) | 返回指定集合中指定对象的出现次数 |
replaceAll(List list, Object old, Object new) | 替换 |
同步控制
Collections 工具类中提供了多个synchronized方法,该方法返回指定集合对象对应的同步对象,从而解决多线程并发访问集合时线程的安全问题。HashSet、ArrayList、HashMap都是线程不安全的,如果需要考虑同步,则使用这些方法,这些方法右:synchronizedSet、 synchronizedSortedSet、 synchronizedList、synchronizedMap、synchronizedSortedMap
特别需要指出的是,在使用迭代方法遍历集合时需要手工同步返回的集合
设置不可变集合
Collections 有三类方法返回一个不可变集合
empty() 返回一个空的不可变的集合对象
singleton() 返回一个只包含指定对象的,不可变集合对象
unmodifiable() 返回指定集合对象的不可变视图
其他
disjoint 如果两个指定collection中没有相同的元素,则返回true
addAll 一种方便的方式,将所有指定元素添加到指定的collection 中
Comparator 返回一个比较器,它强行反转指定比较器的顺序。如果指定比较器为null,则此方法等同于reverseOrder (换句话说,它返回一个比较器,该比较器将强行反转实现COmparable接口那些对象,collection
上的自然顺序)
Optional 容器类
这是一个可以为null 的容器对象,如果值存在则isPresent() 方法会返回true, 调用get()方法会返回该对象
of: 为非null的值创建一个Optional
ofNullable: 为指定的值创建一个Optional, 如果指定的值为null, 则返回一个空的Optional
isPresent: 如果值存在返回true, 否则返回false
get 如果值存在返回true, 否则返回false
ifPresent: 如果Optional 实例有值则为其调用consumer, 否则不做处理
orElse 如果有值则将其返回,否则返回指定的其他值
orElseGet: 和orElse 方法类似,区别在于得到的默认值,orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier 接口的实现用来生成默认值
orElseThrow: 如果有值则将其返回,否则抛出supplier 接口创建的异常
map: 如果有值,则对其执行调用mapping 函数得到返回值,如果返回值不为null, 则创建包含mapping 返回值的optional 作为map方法返回值,否则返回空optional
flatMap: 如果有值,为其执行mapping 函数返回Optional 类型返回值,否则返回空Optional。 flatMap与map方法类似,区别在于flatMap 中的mapper 返回值必须是Optional。 调用结束时,flatMap 不会对结果用Optional 封装
filter: 如果有值并满足断言条件返回包含该值的optional, 否则返回空Optional
public class OptionalDemo {
public static void main(String[] args){
Optional<String> optional = Optional.of("bin");
Optional<String> optional2 = Optional.ofNullable("bin");
Optional<String> optional3 = optional.empty();
System.out.println(optional.isPresent());
System.out.println(optional.get());
optional.ifPresent((value)-> System.out.println(value));
System.out.println(optional3.orElse("hahah"));
System.out.println(optional3.orElseGet(()->"default"));
Optional<String> optional4 = optional.map((value)->value.toUpperCase());
System.out.println(optional4.orElse("not found"));
Optional optional5 = optional.flatMap((value)->Optional.of(value.toUpperCase()+" flatMap"));
System.out.println(optional5.orElse("not found"));
System.out.println(optional.filter((v)->v.length()>3).orElse("这个值的长度小于4"));
}
Queue 和Deque
队列(queue)是一种特殊的线性表,是一种先进先出的数据结构,它允许在表的前端进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾。进行删除操作的端称为队头,队列中没有元素时,称为空队列。LinkedList 实现了Deque 接口
addFirst 是在前端进行插入操作, 使用poll 操作取出前端的值
Deque
peek 是查询first, poll 是移除first
getFirst() 就是得到最先add 进入的值
迭代器设计模式
提供一个方法按顺序遍历一个集合内的元素,而又不需要暴露该对象的内部表示
- 访问一个聚合的对象,而不需要暴露对象的内部表示
- 支持对聚合对象的多种遍历
- 对遍历不同的对象,提供统一的接口
guava 对集合的支持
Guava 工程包含了若干个被 google 的java 项目广泛依赖的核心库,例如集合(collections), 缓存(caching),原生类型支持(primitives support), 并发库(concurrency libraries), 通用注解(common annotations)、 字符串处理(string processing), I/O等等,
1、 只读设置
@Test
public void testGuava1() {
System.out.println("test guava");
List<String> list1 = Arrays.asList("jack","tom","lily","bin");
// list.add("vicne");
List<String> list2 = new ArrayList<>();
list2.add("1");
list2.add("2");
List<String> readList = Collections.unmodifiableList(list2);
// readList.add("vince");
ImmutableList<String> iList = ImmutableList.of("jack","tom","lily");
// iList.add("vince");
}
2、函数式编程: 过滤器
@Test
public void testGuava2(){
List<String> list = Lists.newArrayList("java","php","jack");
Collection<String> res = Collections2.filter(list,(e)->e.startsWith("j"));
res.forEach(System.out::println);
}
3、函数式编程:转换
@Test
public void testGuava3(){
Set<Long> timeSet = Sets.newHashSet(20121212L,20170520L,20120403L);
Collection<String> timeCollect = Collections2.
transform(timeSet,(e)-> new SimpleDateFormat("yyyy-mm-dd").format(e));
timeCollect.forEach(System.out::println);
}
@Test
public void testGuava4(){
List<String> list = Lists.newArrayList("java","php","jack","h5");
Function<String,String> f1 = new Function<String,String>(){
@Override
public String apply(String t) {
return t.length()>4?t.substring(0,4):t;
}
};
Function<String,String> f2 = new Function<String, String>() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
};
Function<String,String> f = Functions.compose(f1,f2);
Collection<String> coll = Collections2.transform(list,f);
coll.forEach(System.out::println);
}
4、组合式函数编程
@Test
public void testGuava4() {
List<String> list = Lists.newArrayList("java", "php", "jack", "h5");
Function<String, String> f1 = new Function<String, String>() {
@Override
public String apply(String t) {
return t.length() > 4 ? t.substring(0, 4) : t;
}
};
Function<String, String> f2 = new Function<String, String>() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
};
Function<String, String> f = Functions.compose(f1, f2);
Collection<String> coll = Collections2.transform(list, f);
coll.forEach(System.out::println);
}
5、加入约束:非空、长度验证
14版本之后移除了Constraint 类
6、 集合操作: 交集、差集、并集
@Test
public void testGuava6() {
Set<Integer> set1 = Sets.newHashSet(1, 2, 3);
Set<Integer> set2 = Sets.newHashSet(2, 3, 4);
// 交集
Sets.SetView<Integer> v1 = Sets.intersection(set1, set2);
v1.forEach(s -> System.out.print(s + " "));
System.out.println();
// 差集
Sets.SetView<Integer> v2 = Sets.difference(set1, set2);
v1.forEach(s -> System.out.print(s + " "));
System.out.println();
// 并集
Sets.SetView<Integer> v3 = Sets.union(set1, set2);
v3.forEach(s -> System.out.print(s + " "));
System.out.println();
}
7、Multiset:无序可重复
@Test
public void testGuava7() {
String s = "good good study day day up";
String[] ss = s.split(" ");
HashMultiset<String> set = HashMultiset.create();
for (String str : ss) {
set.add(str);
}
Set<String> set2 = set.elementSet();
for (String str : set2) {
System.out.println(str + ":" + set.count(str));
}
}
8、Multimap key可以重复
@Test
public void testGuava8() {
Map<String, String> map = new HashMap<>();
map.put("Java从入门到精通", "bin");
map.put("Android从入门到精通", "bin");
map.put("PHP从入门到精通", "jack");
map.put("笑看人生", "vince");
Multimap<String, String> mmap = ArrayListMultimap.create();
Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, String> entry = iter.next();
mmap.put(entry.getValue(), entry.getKey());
}
Set<String> keySet = mmap.keySet();
for (String key : keySet) {
System.out.println(key + " " + mmap.get(key).toString());
}
}
9、 BiMap: 双向Map(bidirectional Map)键与值不能重复
@Test
public void testGuava9() {
BiMap<String, String> map = HashBiMap.create();
map.put("finally_test", "18800208123");
map.put("bin", "18800208234");
System.out.println(map.inverse().get("18800208123"));
for (Map.Entry e : map.entrySet()) {
System.out.println(e.getKey() + " " + e.getValue());
}
}
10、 双键的Map table rowKey+columnKey+value
@Test
public void testGuava10() {
Table<String, String, Integer> table = HashBasedTable.create();
table.put("jack", "java", 80);
table.put("tom", "php", 70);
table.put("bin", "java", 59);
table.put("lily", "ui", 98);
Set<Table.Cell<String, String, Integer>> cells = table.cellSet();
for (Table.Cell c : cells) {
System.out.println(c.getRowKey() + " " + c.getColumnKey() + " " + c.getValue());
}
}