@nextleaf
2018-08-20T01:20:58.000000Z
字数 11317
阅读 810
Java Map 泛型 集合
今天内容:
Set集合,无序不重复,元素可为null
| Modifier and Type | Method and Description |
|---|---|
boolean |
add(E e)
Adds the specified element to this set if it is not already present.
|
void |
clear()
Removes all of the elements from this set.
|
Object
|
clone()
Returns a shallow copy of this HashSet instance: the elements
themselves are not cloned.
|
boolean |
contains
(
Object
o)
Returns true if this set contains the specified element.
|
boolean |
isEmpty()
Returns true if this set contains no elements.
|
Iterator<E> |
iterator()
Returns an iterator over the elements in this set.
|
boolean |
remove(Object o)
Removes the specified element from this set if it is present.
|
int |
size()
Returns the number of elements in this set (its cardinality).
|
Spliterator<E> |
spliterator()
Creates a late-binding and fail-fast
Spliterator
over the elements in this set.
|
package com.nl.sx816.CollectionFramework.SetDemo;import java.util.Date;import java.util.HashSet;import java.util.Iterator;import java.util.Objects;/*** Created with IntelliJ IDEA 2018.* Description: Set集合,无序不重复,元素可为null* 不同步* @author: 黄昭鸿* @date: 2018-08-17* Time: 9:23*/public class SetDemo {public static void main(String[] args) {HashSet hashSet = new HashSet();hashSet.add("A");hashSet.add("B");hashSet.add("C");hashSet.add("D");hashSet.add(new S("huang", 12, new Date()));hashSet.add(new S("huang", 12, new Date()));hashSet.add(new S("sun", 13, new Date()));hashSet.add(new S("wang", 14, new Date()));hashSet.add(new S());hashSet.add(new S());hashSet.add(null);//迭代器Iterator iterator = hashSet.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}//普通迭代器System.out.println("普通迭代器:");hashSet.iterator().forEachRemaining(System.out::print);System.out.println(hashSet.size());hashSet.remove(null);System.out.println(hashSet.size());//forEach()hashSet.forEach(System.out::print);}}class S {String str;int i;Date date;public S() {}public S(String str, int i, Date date) {this.str = str;this.i = i;this.date = date;}@Overridepublic String toString() {if (this.str == null && this.date == null) {return "名字null" + ",年龄" + i + ",日期null";} else if (this.str == null) {return "名字null" + ",年龄" + i + ",日期" + date.toString();} else if (this.date == null) {return "名字" + str + ",年龄" + i + ",日期null";} else {return "名字" + str + ",年龄" + i + ",日期" + date.toString();}}@Overridepublic int hashCode() {/* if (str == null) {return "".hashCode() + i;} else {return str.hashCode() + i;}*/return Objects.requireNonNullElse(str, "").hashCode() + i;}@Overridepublic boolean equals(Object obj) {if (obj instanceof S) {S s = (S) obj;return (this.str == null || this.str.equals(s.str)) && this.i == (s.i);} else {return false;}}}
基于哈希表的Map接口实现。
请注意,此实现不同步。
HashMap 的查找效率贼高
键、值允许为null,键不允许重复,后者覆盖前者
transient Node<K,V>[] table;,当发生 哈希冲突(碰撞)的时候,HashMap 采用 拉链法 进行解决),JDK 8 后又加了 红黑树1.默认初始容量:16,必须是 2 的整数次方
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
哈希表容量取 2 的整数次幂,有以下 2 点好处:
1.使用减法替代取模,提升计算效率;
2.为了使不同 hash 值发生碰撞的概率更小,尽可能促使元素在哈希表中均匀地散列
2.默认加载因子的大小:0.75,可不是随便的,结合时间和空间效率考虑得到的
static final float DEFAULT_LOAD_FACTOR = 0.75f;
3.最大容量: 2^ 30 次方
static final int MAXIMUM_CAPACITY = 1 << 30;
4.当前 HashMap 修改的次数,这个变量用来保证 fail-fast 机制
transient int modCount;
5.阈值,下次需要扩容时的值,等于 容量*加载因子
int threshold;
6.树形阈值:JDK 1.8 新增的,当使用 树 而不是列表来作为桶时使用。必须必 2 大
static final int TREEIFY_THRESHOLD = 8;
7.非树形阈值:也是 1.8 新增的,扩容时分裂一个树形桶的阈值(?不是很懂 - -),要比 TREEIFY_THRESHOLD 小
static final int UNTREEIFY_THRESHOLD = 6;
8.树形最小容量:桶可能是树的哈希表的最小容量。至少是 TREEIFY_THRESHOLD 的 4 倍,这样能避免扩容时的冲突
static final int MIN_TREEIFY_CAPACITY = 64;
9.缓存的 键值对集合(另外两个视图:keySet 和 values 是在 AbstractMap 中声明的)
transient Set<Map.Entry<K,V>> entrySet;
10.哈希表中的链表数组
transient Node<K,V>[] table;
当发生 哈希冲突(碰撞)的时候,HashMap 采用 拉链法 进行解决
11.键值对的数量
transient int size;
12.哈希表的加载因子
final float loadFactor;
由于 HashMap 扩容开销很大(需要创建新数组、重新哈希、分配等等,最好在初始化的时候就指定好 HashMap 的容量),因此与扩容相关的两个因素:
成为了 HashMap 最重要的部分之一,它们决定了 HashMap 什么时候扩容。
HashMap 的默认加载因子为 0.75,这是在时间、空间两方面均衡考虑下的结果:
当设置初始容量时,需要提前考虑Map中可能有多少对键值对,设计合理的加载因子,尽可能避免进行扩容。
如果存储的键值对很多,干脆设置个大点的容量,这样可以少扩容几次。
HashMap()
使用默认初始容量(16)和默认加载因子(0.75)构造一个空的HashMap。
HashMap(int initialCapacity)
使用指定的初始容量和默认加载因子(0.75)构造一个空的HashMap,容量必须是 2 的整数次方,假如你传入的是 5,返回的初始容量为 8 。
HashMap(int initialCapacity, float loadFactor)
使用指定的初始容量和加载因子构造一个空的HashMap。
HashMap(Map<? extends K,? extends V> m)
使用与指定Map相同的映射构造一个新的HashMap。
hash():计算对应的位置
resize():扩容
putTreeVal():树形节点的插入
treeifyBin():树形化容器
HashMap 中通过将传入键的 hashCode 进行无符号右移 16 位,然后进行按位异或,得到这个键的哈希值。
static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
//Java8特有的map遍历方式map.forEach((k, v) -> Console.log("key:value = " + k + ":" + v));
package com.nl.sx816.CollectionFramework.MapDemo;import cn.hutool.core.lang.Console;import java.util.*;/*** Created with IntelliJ IDEA 2018.* Description: HashMap。键、值允许为null,键不允许重复,后者覆盖前者* 不同步** @author: 黄昭鸿* @date: 2018-08-17* Time: 09:00*/public class HashMapDemo {public static void main(String[] args){HashMap hashMap=newHashMap();//线程安全的 MapMap m = Collections.synchronizedMap(newHashMap());//containsKey(Object key)System.out.print(hashMap.containsKey(2));//get(Object key)返回指定键映射到的值System.out.println(hashMap.get(2));//Set<K> keySet()返回Set此映射中包含的键的视图。//Set<String> set=hashMap.keySet();//Collection<V> values()返回Collection此映射中包含的值的视图。//Collection<String> list=hashMap.values();//Set<Map.Entry<K,V>> entrySet()dddd(hashMap);}public static void dddd(HashMap map){//迭代器System.out.println("迭代器");map.entrySet().iterator().forEachRemaining(System.out::print);System.out.println("Java8特有的map遍历方式");map.forEach((k, v) -> System.out.println("key:value = " + k + ":" + v));//Java8特有的map遍历方式//map.forEach((k, v) -> Console.log("key:value = " + k + ":" + v));//Java8,通过Map.entrySet遍历key和value,在大容量时推荐使用//map.entrySet().forEach(entry -> Console.log("key:value = " + entry.getKey() + ":" + entry.getValue()));//Java8,通过Map.entrySet使用Iterator遍历key和value//map.entrySet().iterator().forEachRemaining(item -> System.out.println("key=value:" + item));//Java8,通过Map.keySet遍历key和value//map.keySet().forEach(key -> System.out.println("map.get(" + key + ") = " + map.get(key)));//Java8,遍历所有的value//map.values().forEach(System.out::println);}public static HashMap newHashMap(){HashMap hashMap=new HashMap();hashMap.put(1,"荆州");hashMap.put(2,"杭州");hashMap.put(3,"苏州");hashMap.put(4,"庐州");hashMap.put(null,"北京");hashMap.put(5,null);return hashMap;}}
泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数
每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。
注意类型参数只能代表引用型类型,不能是原始类型
package com.nl.sx817.generics;/*** Created with IntelliJ IDEA 2018.* Description: Java泛型* 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。* 注意类型参数只能代表引用型类型,不能是原始类型** @author: 黄昭鸿* @date: 2018-08-17* Time: 14:01*/public class GenericMethodTest {/*** Description: 泛型方法 printArray** @param [inputArray]* @return void* @author 黄昭鸿* @date 2018/8/17 14:02*/public static void main(String args[]) {// 创建不同类型数组: Integer, Double 和 CharacterInteger[] intArray = {1, 2, 3, 4, 5};Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};Character[] charArray = {'H', 'E', 'L', 'L', 'O'};System.out.println("整型数组元素为:");// 传递一个整型数组printArray(intArray);System.out.println("\n双精度型数组元素为:");// 传递一个双精度型数组printArray(doubleArray);System.out.println("\n字符型数组元素为:");// 传递一个字符型数组printArray(charArray);}public static <E> void printArray(E[] inputArray) {// 输出数组元素for (E element : inputArray) {System.out.printf("%s ", element);}System.out.println();}}
有界的类型参数
package com.nl.sx817.generics;import cn.hutool.core.util.RandomUtil;import java.math.RoundingMode;import java.util.ArrayList;import java.util.List;import java.util.ListIterator;/*** Created with IntelliJ IDEA 2018.* Description:Java泛型——有界的类型参数** @author: 黄昭鸿* @date: 2018-08-17* Time: 14:14*/public class MaximumTest {/*** Description: 要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。* 比较三个值并返回最大值** @param [x, y, z]* @return T* @author 黄昭鸿* @date 2018/8/17 14:15*/public static <T extends Comparable<T>> T maximum(T x, T y, T z) {// 假设x是初始最大值T max = x;if (y.compareTo(max) > 0) {//y 更大max = y;}if (z.compareTo(max) > 0) {// 现在 z 更大max = z;}// 返回最大对象return max;}public static void main(String args[]) {System.out.printf("%d, %d 和 %d 中最大的数为 %d\n\n", 3, 4, 5, maximum(3, 4, 5));System.out.printf("%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));System.out.printf("%s, %s 和 %s 中最大的数为 %s\n", "pear", "apple", "orange", maximum("pear", "apple", "orange"));//List<?> name = newList("String");List<?> age = newList("Integer");List<?> number = newList("Number");List<?> gender = newList("Character");List<?> height = newList("Double");List<?> ggList = newList("Gg");getData(name);getData(age);getData(number);getData(gender);getData(height);getData(ggList);}public static void getData(List<?> data) {ListIterator listIterator = data.listIterator();while (listIterator.hasNext()) {System.out.println(listIterator.next());}}//初始化数据public static List<?> newList(String type) {switch (type) {case "String":List names = new ArrayList<String>();names.add("及时雨");names.add("玉麒麟");names.add("智多星");names.add("入云龙");names.add("豹子头");names.add("小李广");names.add("花和尚");names.add("神行太保");names.add("黑旋风");names.add("浪里白条");names.add("拼命三郎");return names;case "Integer":List age = new ArrayList<String>();for (int i = 30; i < 41; i++) {age.add(i);}return age;case "Number":List number = new ArrayList<String>();for (int i = 0; i < 11; i++) {number.add(i);}return number;case "Character":List gender = new ArrayList<String>();gender.add('男');gender.add('女');gender.add('男');gender.add('女');gender.add('男');gender.add('女');gender.add('男');gender.add('女');gender.add('男');gender.add('女');gender.add('男');return gender;case "Double":List height = new ArrayList<String>();height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));return height;case "Gg":List gg = new ArrayList<Gg>();gg.add(new Gg("天魁星","呼保义","总兵都头领"));gg.add(new Gg("天罡星","玉麒麟","总兵都头领"));gg.add(new Gg("天机星","智多星","掌管机密军师"));gg.add(new Gg("天闲星","入云龙","掌管机密军师"));gg.add(new Gg("天雄星","豹子头","马军五虎将"));gg.add(new Gg("天英星","小李广","马军八骠骑"));gg.add(new Gg("天孤星","花和尚","步军头领"));gg.add(new Gg("天速星","神行太保","总探声息头领"));gg.add(new Gg("天杀星","黑旋风","步军头领"));gg.add(new Gg("天慧星","拼命三郎","歩军头领"));gg.add(new Gg("天损星","浪里白条","水军头领"));return gg;default:return new ArrayList();}}}class Gg {String constellation="星宿",nickname="诨名",post="职位";public Gg() {}public Gg(String constellation, String nickname, String post) {this.constellation = constellation;this.nickname = nickname;this.post = post;}@Overridepublic String toString() {return "好汉{" +"星宿='" + constellation + '\'' +", 诨名='" + nickname + '\'' +", 职位='" + post + '\'' +'}';}}