[关闭]
@nextleaf 2018-08-20T09:20:58.000000Z 字数 11317 阅读 691

2018-08-17 工作日志

Java Map 泛型 集合


今天内容:


Set接口

Set集合,无序不重复,元素可为null

HashSet

此类实现Set接口,由哈希表(实际上是HashMap实例)支持

该类允许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.

附:

  1. package com.nl.sx816.CollectionFramework.SetDemo;
  2. import java.util.Date;
  3. import java.util.HashSet;
  4. import java.util.Iterator;
  5. import java.util.Objects;
  6. /**
  7. * Created with IntelliJ IDEA 2018.
  8. * Description: Set集合,无序不重复,元素可为null
  9. * 不同步
  10. * @author: 黄昭鸿
  11. * @date: 2018-08-17
  12. * Time: 9:23
  13. */
  14. public class SetDemo {
  15. public static void main(String[] args) {
  16. HashSet hashSet = new HashSet();
  17. hashSet.add("A");
  18. hashSet.add("B");
  19. hashSet.add("C");
  20. hashSet.add("D");
  21. hashSet.add(new S("huang", 12, new Date()));
  22. hashSet.add(new S("huang", 12, new Date()));
  23. hashSet.add(new S("sun", 13, new Date()));
  24. hashSet.add(new S("wang", 14, new Date()));
  25. hashSet.add(new S());
  26. hashSet.add(new S());
  27. hashSet.add(null);
  28. //迭代器
  29. Iterator iterator = hashSet.iterator();
  30. while (iterator.hasNext()) {
  31. System.out.println(iterator.next());
  32. }
  33. //普通迭代器
  34. System.out.println("普通迭代器:");
  35. hashSet.iterator().forEachRemaining(System.out::print);
  36. System.out.println(hashSet.size());
  37. hashSet.remove(null);
  38. System.out.println(hashSet.size());
  39. //forEach()
  40. hashSet.forEach(System.out::print);
  41. }
  42. }
  43. class S {
  44. String str;
  45. int i;
  46. Date date;
  47. public S() {
  48. }
  49. public S(String str, int i, Date date) {
  50. this.str = str;
  51. this.i = i;
  52. this.date = date;
  53. }
  54. @Override
  55. public String toString() {
  56. if (this.str == null && this.date == null) {
  57. return "名字null" + ",年龄" + i + ",日期null";
  58. } else if (this.str == null) {
  59. return "名字null" + ",年龄" + i + ",日期" + date.toString();
  60. } else if (this.date == null) {
  61. return "名字" + str + ",年龄" + i + ",日期null";
  62. } else {
  63. return "名字" + str + ",年龄" + i + ",日期" + date.toString();
  64. }
  65. }
  66. @Override
  67. public int hashCode() {
  68. /* if (str == null) {
  69. return "".hashCode() + i;
  70. } else {
  71. return str.hashCode() + i;
  72. }*/
  73. return Objects.requireNonNullElse(str, "").hashCode() + i;
  74. }
  75. @Override
  76. public boolean equals(Object obj) {
  77. if (obj instanceof S) {
  78. S s = (S) obj;
  79. return (this.str == null || this.str.equals(s.str)) && this.i == (s.i);
  80. } else {
  81. return false;
  82. }
  83. }
  84. }

Map和泛型

Map接口

HashMap

基于哈希表的Map接口实现。
请注意,此实现不同步
HashMap 的查找效率贼高
键、值允许为null,键不允许重复,后者覆盖前者

HashMap 的特点

HashMap 的 13 个成员变量

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 什么时候扩容。

HashMap 的默认加载因子为 0.75,这是在时间、空间两方面均衡考虑下的结果:

当设置初始容量时,需要提前考虑Map中可能有多少对键值对,设计合理的加载因子,尽可能避免进行扩容。
如果存储的键值对很多,干脆设置个大点的容量,这样可以少扩容几次。

HashMap构造函数和描述

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中的hash()

HashMap 中通过将传入键的 hashCode 进行无符号右移 16 位,然后进行按位异或,得到这个键的哈希值。

  1. static final int hash(Object key) {
  2. int h;
  3. return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  4. }

forEach(BiConsumer action)

  1. //Java8特有的map遍历方式
  2. map.forEach((k, v) -> Console.log("key:value = " + k + ":" + v));

相关博文:Java 集合框架
相关教程:Java泛型

附:

  1. package com.nl.sx816.CollectionFramework.MapDemo;
  2. import cn.hutool.core.lang.Console;
  3. import java.util.*;
  4. /**
  5. * Created with IntelliJ IDEA 2018.
  6. * Description: HashMap。键、值允许为null,键不允许重复,后者覆盖前者
  7. * 不同步
  8. *
  9. * @author: 黄昭鸿
  10. * @date: 2018-08-17
  11. * Time: 09:00
  12. */
  13. public class HashMapDemo {
  14. public static void main(String[] args){
  15. HashMap hashMap=newHashMap();
  16. //线程安全的 Map
  17. Map m = Collections.synchronizedMap(newHashMap());
  18. //containsKey(Object key)
  19. System.out.print(hashMap.containsKey(2));
  20. //get(Object key)返回指定键映射到的值
  21. System.out.println(hashMap.get(2));
  22. //Set<K> keySet()返回Set此映射中包含的键的视图。
  23. //Set<String> set=hashMap.keySet();
  24. //Collection<V> values()返回Collection此映射中包含的值的视图。
  25. //Collection<String> list=hashMap.values();
  26. //Set<Map.Entry<K,V>> entrySet()
  27. dddd(hashMap);
  28. }
  29. public static void dddd(HashMap map){
  30. //迭代器
  31. System.out.println("迭代器");
  32. map.entrySet().iterator().forEachRemaining(
  33. System.out::print
  34. );
  35. System.out.println("Java8特有的map遍历方式");
  36. map.forEach((k, v) -> System.out.println("key:value = " + k + ":" + v));
  37. //Java8特有的map遍历方式
  38. //map.forEach((k, v) -> Console.log("key:value = " + k + ":" + v));
  39. //Java8,通过Map.entrySet遍历key和value,在大容量时推荐使用
  40. //map.entrySet().forEach(entry -> Console.log("key:value = " + entry.getKey() + ":" + entry.getValue()));
  41. //Java8,通过Map.entrySet使用Iterator遍历key和value
  42. //map.entrySet().iterator().forEachRemaining(item -> System.out.println("key=value:" + item));
  43. //Java8,通过Map.keySet遍历key和value
  44. //map.keySet().forEach(key -> System.out.println("map.get(" + key + ") = " + map.get(key)));
  45. //Java8,遍历所有的value
  46. //map.values().forEach(System.out::println);
  47. }
  48. public static HashMap newHashMap(){
  49. HashMap hashMap=new HashMap();
  50. hashMap.put(1,"荆州");
  51. hashMap.put(2,"杭州");
  52. hashMap.put(3,"苏州");
  53. hashMap.put(4,"庐州");
  54. hashMap.put(null,"北京");
  55. hashMap.put(5,null);
  56. return hashMap;
  57. }
  58. }

Java泛型

泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数
每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。
注意类型参数只能代表引用型类型,不能是原始类型

  1. package com.nl.sx817.generics;
  2. /**
  3. * Created with IntelliJ IDEA 2018.
  4. * Description: Java泛型
  5. * 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
  6. * 注意类型参数只能代表引用型类型,不能是原始类型
  7. *
  8. * @author: 黄昭鸿
  9. * @date: 2018-08-17
  10. * Time: 14:01
  11. */
  12. public class GenericMethodTest {
  13. /**
  14. * Description: 泛型方法 printArray
  15. *
  16. * @param [inputArray]
  17. * @return void
  18. * @author 黄昭鸿
  19. * @date 2018/8/17 14:02
  20. */
  21. public static void main(String args[]) {
  22. // 创建不同类型数组: Integer, Double 和 Character
  23. Integer[] intArray = {1, 2, 3, 4, 5};
  24. Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
  25. Character[] charArray = {'H', 'E', 'L', 'L', 'O'};
  26. System.out.println("整型数组元素为:");
  27. // 传递一个整型数组
  28. printArray(intArray);
  29. System.out.println("\n双精度型数组元素为:");
  30. // 传递一个双精度型数组
  31. printArray(doubleArray);
  32. System.out.println("\n字符型数组元素为:");
  33. // 传递一个字符型数组
  34. printArray(charArray);
  35. }
  36. public static <E> void printArray(E[] inputArray) {
  37. // 输出数组元素
  38. for (E element : inputArray) {
  39. System.out.printf("%s ", element);
  40. }
  41. System.out.println();
  42. }
  43. }

有界的类型参数

  1. package com.nl.sx817.generics;
  2. import cn.hutool.core.util.RandomUtil;
  3. import java.math.RoundingMode;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import java.util.ListIterator;
  7. /**
  8. * Created with IntelliJ IDEA 2018.
  9. * Description:Java泛型——有界的类型参数
  10. *
  11. * @author: 黄昭鸿
  12. * @date: 2018-08-17
  13. * Time: 14:14
  14. */
  15. public class MaximumTest {
  16. /**
  17. * Description: 要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
  18. * 比较三个值并返回最大值
  19. *
  20. * @param [x, y, z]
  21. * @return T
  22. * @author 黄昭鸿
  23. * @date 2018/8/17 14:15
  24. */
  25. public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
  26. // 假设x是初始最大值
  27. T max = x;
  28. if (y.compareTo(max) > 0) {
  29. //y 更大
  30. max = y;
  31. }
  32. if (z.compareTo(max) > 0) {
  33. // 现在 z 更大
  34. max = z;
  35. }
  36. // 返回最大对象
  37. return max;
  38. }
  39. public static void main(String args[]) {
  40. System.out.printf("%d, %d 和 %d 中最大的数为 %d\n\n", 3, 4, 5, maximum(3, 4, 5));
  41. System.out.printf("%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));
  42. System.out.printf("%s, %s 和 %s 中最大的数为 %s\n", "pear", "apple", "orange", maximum("pear", "apple", "orange"));
  43. //
  44. List<?> name = newList("String");
  45. List<?> age = newList("Integer");
  46. List<?> number = newList("Number");
  47. List<?> gender = newList("Character");
  48. List<?> height = newList("Double");
  49. List<?> ggList = newList("Gg");
  50. getData(name);
  51. getData(age);
  52. getData(number);
  53. getData(gender);
  54. getData(height);
  55. getData(ggList);
  56. }
  57. public static void getData(List<?> data) {
  58. ListIterator listIterator = data.listIterator();
  59. while (listIterator.hasNext()) {
  60. System.out.println(listIterator.next());
  61. }
  62. }
  63. //初始化数据
  64. public static List<?> newList(String type) {
  65. switch (type) {
  66. case "String":
  67. List names = new ArrayList<String>();
  68. names.add("及时雨");names.add("玉麒麟");
  69. names.add("智多星");names.add("入云龙");
  70. names.add("豹子头");names.add("小李广");
  71. names.add("花和尚");names.add("神行太保");
  72. names.add("黑旋风");names.add("浪里白条");
  73. names.add("拼命三郎");
  74. return names;
  75. case "Integer":
  76. List age = new ArrayList<String>();
  77. for (int i = 30; i < 41; i++) {
  78. age.add(i);
  79. }
  80. return age;
  81. case "Number":
  82. List number = new ArrayList<String>();
  83. for (int i = 0; i < 11; i++) {
  84. number.add(i);
  85. }
  86. return number;
  87. case "Character":
  88. List gender = new ArrayList<String>();
  89. gender.add('男');gender.add('女');gender.add('男');gender.add('女');
  90. gender.add('男');gender.add('女');gender.add('男');gender.add('女');
  91. gender.add('男');gender.add('女');gender.add('男');
  92. return gender;
  93. case "Double":
  94. List height = new ArrayList<String>();
  95. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  96. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  97. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  98. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  99. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  100. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  101. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  102. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  103. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  104. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  105. height.add(RandomUtil.randomDouble(1.5, 2.1, 2, RoundingMode.HALF_UP));
  106. return height;
  107. case "Gg":
  108. List gg = new ArrayList<Gg>();
  109. gg.add(new Gg("天魁星","呼保义","总兵都头领"));
  110. gg.add(new Gg("天罡星","玉麒麟","总兵都头领"));
  111. gg.add(new Gg("天机星","智多星","掌管机密军师"));
  112. gg.add(new Gg("天闲星","入云龙","掌管机密军师"));
  113. gg.add(new Gg("天雄星","豹子头","马军五虎将"));
  114. gg.add(new Gg("天英星","小李广","马军八骠骑"));
  115. gg.add(new Gg("天孤星","花和尚","步军头领"));
  116. gg.add(new Gg("天速星","神行太保","总探声息头领"));
  117. gg.add(new Gg("天杀星","黑旋风","步军头领"));
  118. gg.add(new Gg("天慧星","拼命三郎","歩军头领"));
  119. gg.add(new Gg("天损星","浪里白条","水军头领"));
  120. return gg;
  121. default:
  122. return new ArrayList();
  123. }
  124. }
  125. }
  126. class Gg {
  127. String constellation="星宿",nickname="诨名",post="职位";
  128. public Gg() {}
  129. public Gg(String constellation, String nickname, String post) {
  130. this.constellation = constellation;
  131. this.nickname = nickname;
  132. this.post = post;
  133. }
  134. @Override
  135. public String toString() {
  136. return "好汉{" +
  137. "星宿='" + constellation + '\'' +
  138. ", 诨名='" + nickname + '\'' +
  139. ", 职位='" + post + '\'' +
  140. '}';
  141. }
  142. }

#

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注