容器【双例集合、TreeMap容器的使用、 Iterator接口、Collections工具类】(四)-全面详解(学习总结---从入门到深化)

灰太狼 2023-10-13 15:44 41阅读 0赞

" class="reference-link">174acfb63e1e4d349ebb0149513bed12.gif

29ec4a888e384f76bfdf06fb7870c84a.png

目录

通过元素自身实现比较规则

通过比较器实现比较规则

双例集合

TreeMap容器的使用

Iterator接口

Collections工具类


通过元素自身实现比较规则

8084f2532477425f978b67b4cbf69208.png

在元素自身实现比较规则时,需要实现Comparable接口中的 compareTo方法,该方法中用来定义比较规则。TreeSet通过调用 该方法来完成对元素的排序处理。

创建Users类

  1. public class Users implements Comparable<Users>{
  2. private String username;
  3. private int userage;
  4. public Users(String username, intuserage) {
  5. this.username = username;
  6. this.userage = userage;
  7. }
  8. public Users() { }
  9. @Override
  10. public boolean equals(Object o) {
  11. System.out.println("equals...");
  12. if (this == o) return true;
  13. if (o == null || getClass() != o.getClass()) return false;
  14. Users users = (Users) o;
  15. if (userage != users.userage) return false;
  16. return username != null ? username.equals(users.username) : users.username == null;
  17. }
  18. @Override
  19. public int hashCode() {
  20. int result = username != null ? username.hashCode() : 0;
  21. result = 31 * result + userage;
  22. return result;
  23. }
  24. public String getUsername() {
  25. return username;
  26. }
  27. public void setUsername(String username)
  28. {
  29. this.username = username;
  30. }
  31. public int getUserage() {
  32. return userage;
  33. }
  34. public void setUserage(int userage) {
  35. this.userage = userage;
  36. }
  37. @Override
  38. public String toString() {
  39. return "Users{" +
  40. "username='" + username + '\'' +
  41. ", userage=" + userage +
  42. '}';
  43. }
  44. //定义比较规则
  45. //正数:大,负数:小,0:相等
  46. @Override
  47. public int compareTo(Users o) {
  48. if(this.userage > o.getUserage()){
  49. return 1;
  50. }
  51. if(this.userage == o.getUserage()){
  52. return this.username.compareTo(o.getUsername());
  53. }
  54. return -1;
  55. }
  56. }
  57. Set<Users> set1 = new TreeSet<>();
  58. Users u = new Users("oldlu",18);
  59. Users u1 = new Users("admin",22);
  60. Users u2 = new Users("sxt",22);
  61. set1.add(u);
  62. set1.add(u1);
  63. set1.add(u2);
  64. for(Users users:set1){
  65. System.out.println(users);
  66. }

通过比较器实现比较规则

a16592f90b114bbabf49c6b1ad5dbddc.png

通过比较器定义比较规则时,我们需要单独创建一个比较器,比较 器需要实现Comparator接口中的compare方法来定义比较规则。 在实例化TreeSet时将比较器对象交给TreeSet来完成元素的排序处 理。此时元素自身就不需要实现比较规则了。

创建Student类

  1. public class Student {
  2. private String name;
  3. private int age;
  4. public Student(String name, int age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. public Student() { }
  9. @Override
  10. public String toString() {
  11. return "Student{" +
  12. "name='" + name + '\'' +
  13. ", age=" + age +
  14. '}';
  15. }
  16. public String getName() {
  17. return name;
  18. }
  19. public void setName(String name) {
  20. this.name = name;
  21. }
  22. public int getAge() {
  23. return age;
  24. }
  25. public void setAge(int age) {
  26. this.age = age;
  27. }
  28. @Override
  29. public boolean equals(Object o) {
  30. if (this == o) return true;
  31. if (o == null || getClass() != o.getClass()) return false;
  32. Student student = (Student) o;
  33. if (age != student.age) return false;
  34. return name != null ? name.equals(student.name) : student.name == null;
  35. }
  36. @Override
  37. public int hashCode() {
  38. int result = name != null ? name.hashCode() : 0;
  39. result = 31 * result + age;
  40. return result;
  41. }
  42. }

创建比较器

  1. public class StudentComparator implements Comparator<Student> {
  2. //定义比较规则
  3. @Override
  4. public int compare(Student o1, Student o2) {
  5. if(o1.getAge() > o2.getAge()){
  6. return 1;
  7. }
  8. if(o1.getAge() == o2.getAge()){
  9. return o1.getName().compareTo(o2.getName());
  10. }
  11. return -1;
  12. }
  13. }
  14. public class TreeSetTest3 {
  15. public static void main(String[] args) {
  16. //创建TreeSet容器,并给定比较器对象
  17. Set<Student> set = new TreeSet<>(new StudentComparator());
  18. Student s = new Student("oldlu",18);
  19. Student s1 = new Student("admin",22);
  20. Student s2 = new Student("sxt",22);
  21. set.add(s);
  22. set.add(s1);
  23. set.add(s2);
  24. for(Student student:set){
  25. System.out.println(student);
  26. }
  27. }
  28. }

TreeSet底层源码分析

成员变量

  1. /**
  2. * The backing map.
  3. */
  4. private transient NavigableMap<E,Object> m;
  5. // Dummy value to associate with an Object in the backing Map
  6. private static final Object PRESENT = new Object();

构造方法

  1. public TreeSet() {
  2. this(new TreeMap<E,Object>());
  3. }

添加元素

  1. /**
  2. * Adds the specified element to this set if it is not already present.
  3. * More formally, adds the specified element <tt>e</tt> to this set if
  4. * this set contains no element <tt>e2</tt> such that
  5. * <tt>(e==null ? e2==null : e.equals(e2))</tt>.
  6. * If this set already contains the element, the call leaves the set
  7. * unchanged and returns <tt>false</tt>.
  8. *
  9. * @param e element to be added to this set
  10. * @return <tt>true</tt> if this set did not already contain the specified
  11. * element
  12. */
  13. public boolean add(E e) {
  14. return map.put(e, PRESENT)==null;
  15. }

单例集合使用案例

需求: 产生1-10之间的随机数([1,10]闭区间),将不重复的10个随机数放到 容器中。

使用List类型容器实现

  1. public class ListDemo {
  2. public static void main(String[] args) {
  3. List<Integer> list = new ArrayList<>();
  4. while(true){
  5. //产生随机数
  6. int num = (int) (Math.random()*10+1);
  7. //判断当前元素在容器中是否存在
  8. if(!list.contains(num)){
  9. list.add(num);
  10. }
  11. //结束循环
  12. if(list.size() == 10){
  13. break;
  14. }
  15. }
  16. for(Integer i:list){
  17. System.out.println(i);
  18. }
  19. }
  20. }

使用Set类型容器实现

  1. public class SetDemo {
  2. public static void main(String[] args) {
  3. Set<Integer> set = new HashSet<>();
  4. while(true){
  5. int num = (int)(Math.random()*10+1);
  6. //将元素添加容器中,由于Set类型容器是、不允许有重复元素的,所以不需要判断。
  7. set.add(num);
  8. //结束循环
  9. if(set.size() == 10){
  10. break;
  11. }
  12. }
  13. for(Integer i:set){
  14. System.out.println(i);
  15. }
  16. }
  17. }

双例集合

7788923995b647398df0b591f0bd0bd0.png

Map接口介绍

Map接口定义了双例集合的存储特征,它并不是Collection接口的 子接口。双例集合的存储特征是以key与value结构为单位进行存 储。体现的是数学中的函数 y=f(x)感念。

Map与Collecton的区别:

1、Collection中的容器,元素是孤立存在的(理解为单身),向集 合中存储元素采用一个个元素的方式存储。

2、Map中的容器,元素是成对存在的(理解为现代社会的夫妻)。每 个元素由键与值两部分组成,通过键可以找对所对应的值。

3、Collection中的容器称为单列集合,Map中的容器称为双列集 合。

4、Map中的集合不能包含重复的键,值可以重复;每个键只能对应 一个值。

5、Map中常用的容器为HashMap,TreeMap等。

Map接口中常用的方法表

c03a66694dcb4b5fa4c0c42f1508f61d.png

HashMap容器的使用

HashMap采用哈希算法实现,是Map接口最常用的实现类。 由于 底层采用了哈希表存储数据,我们要求键不能重复,如果发生重 复,新的键值对会替换旧的键值对。 HashMap在查找、删除、修 改方面都有非常高的效率。

  1. public class HashMapTest {
  2. public static void main(String[] args) {
  3. //实例化HashMap容器
  4. Map<String,String> map = new HashMap<>();
  5. //添加元素
  6. map.put("a","A");
  7. map.put("b","B");
  8. map.put("c","C");
  9. map.put("a","D");
  10. //获取容器中元素数量
  11. int size = map.size();
  12. System.out.println(size);
  13. System.out.println("---------------");
  14. //获取元素
  15. //方式一
  16. String v = map.get("a");
  17. System.out.println(v);
  18. System.out.println("---------------");
  19. //方式二
  20. Set<String> keys = map.keySet();
  21. for(String key:keys){
  22. String v1 = map.get(key);
  23. System.out.println(key+" ----"+v1);
  24. }
  25. System.out.println("-------------------");
  26. //方式三
  27. Set<Map.Entry<String,String>> entrySet = map.entrySet();
  28. for(Map.Entry<String,String> entry:entrySet){
  29. String key = entry.getKey();
  30. String v2 = entry.getValue();
  31. System.out.println(key+" ---------- "+v2);
  32. }
  33. System.out.println("--------------------");
  34. //Map容器的并集操作
  35. Map<String,String> map2 = new HashMap<>();
  36. map2.put("f","F");
  37. map2.put("c","CC");
  38. map.putAll(map2);
  39. Set<String> keys2 = map.keySet();
  40. for(String key:keys2){
  41. System.out.println("key: "+key+" Value: "+map.get(key));
  42. }
  43. System.out.println("---------------");
  44. //删除元素
  45. String v3 = map.remove("a");
  46. System.out.println(v3);
  47. Set<String> keys3 = map.keySet();
  48. for(String key:keys3){
  49. System.out.println("key: "+key+" Value: "+map.get(key));
  50. }
  51. System.out.println("-------------------");
  52. //判断Key是否存在
  53. boolean b = map.containsKey("b");
  54. System.out.println(b);
  55. //判断Value是否存在
  56. boolean cc = map.containsValue("CC");
  57. System.out.println(cc);
  58. }
  59. }

HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不 过HashTable的方法添加了synchronized关键字确保线程同步检 查,效率较低。

HashMap与HashTable的区别

1 HashMap: 线程不安全,效率高。允许key或value为null

2 HashTable: 线程安全,效率低。不允许key或value为null

HashMap的底层源码分析

底层存储介绍

HashMap底层实现采用了哈希表,这是一种非常重要的数据结构。 对于我们以后理解很多技术都非常有帮助。 数据结构中由数组和链表来实现对数据的存储,他们各有特点。

(1) 数组:占用空间连续。 寻址容易,查询速度快。但是,增加和 删除效率非常低。

(2) 链表:占用空间不连续。 寻址困难,查询速度慢。但是,增加 和删除效率非常高。 那么,我们能不能结合数组和链表的优点(即查询快,增删效率也 高)呢? 答案就是“哈希表”。 哈希表的本质就是“数组+链表”。

15a98b888069487e8b7ba20685a969fc.png

Oldlu建议

对于本章中频繁出现的“底层实现”讲解,建议学有余力的童鞋将 它搞通。刚入门的童鞋如果觉得有难度,可以暂时跳过。入门 期间,掌握如何使用即可,底层原理是扎实内功,便于大家应 对一些大型企业的笔试面试。

HashMap中的成员变量

  1. /**
  2. * The default initial capacity - MUST be a power of two.
  3. */
  4. static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
  5. /**
  6. * The maximum capacity, used if a higher value is implicitly specified
  7. * by either of the constructors with arguments.
  8. * MUST be a power of two <= 1<<30.
  9. */
  10. static final int MAXIMUM_CAPACITY = 1 << 30;
  11. /**
  12. * The load factor used when none specified in constructor.
  13. */
  14. static final float DEFAULT_LOAD_FACTOR = 0.75f;
  15. /**
  16. * The bin count threshold for using a tree rather than list for a
  17. * bin. Bins are converted to trees when adding an element to a
  18. * bin with at least this many nodes. The value must be greater
  19. * than 2 and should be at least 8 to mesh with assumptions in
  20. * tree removal about conversion back to plain bins upon
  21. * shrinkage.
  22. */
  23. static final int TREEIFY_THRESHOLD = 8;
  24. /**
  25. * The bin count threshold for untreeifying a (split) bin during a
  26. * resize operation. Should be less than TREEIFY_THRESHOLD, and at
  27. * most 6 to mesh with shrinkage detection under removal.
  28. */
  29. static final int UNTREEIFY_THRESHOLD = 6;
  30. /**
  31. * The smallest table capacity for which bins may be treeified.
  32. * (Otherwise the table is resized if too many nodes in a bin.)
  33. * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
  34. * between resizing and treeification thresholds.
  35. */
  36. static final int MIN_TREEIFY_CAPACITY = 64;
  37. /**
  38. * The number of key-value mappings contained in this map.
  39. */
  40. transient int size;
  41. /**
  42. * The table, initialized on first use, and resized as
  43. * necessary. When allocated, length is always a power of two.
  44. * (We also tolerate length zero in some operations to allow
  45. * bootstrapping mechanics that are currently not needed.)
  46. */
  47. transient Node<K,V>[] table;

HashMap中存储元素的节点类型

Node类

  1. static class Node<K,V> implements
  2. Map.Entry<K,V> {
  3. final int hash;
  4. final K key;
  5. V value;
  6. Node<K,V> next;
  7. Node(int hash, K key, V value, Node<K,V> next) {
  8. this.hash = hash;
  9. this.key = key;
  10. this.value = value;
  11. this.next = next;
  12. }
  13. public final K getKey() { return key; }
  14. public final V getValue() { return value; }
  15. public final String toString() { return key + "=" + value; }
  16. public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); }
  17. public final V setValue(V newValue) {
  18. V oldValue = value;
  19. value = newValue;
  20. return oldValue;
  21. }
  22. public final boolean equals(Object o) {
  23. if (o == this)
  24. return true;
  25. if (o instanceof Map.Entry) {
  26. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  27. if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
  28. return true;
  29. }
  30. return false;
  31. }
  32. }

TreeNode类

  1. /**
  2. * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
  3. * extends Node) so can be used as extension of either regular or
  4. * linked node.
  5. */
  6. static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
  7. TreeNode<K,V> parent; // red-black tree links
  8. TreeNode<K,V> left;
  9. TreeNode<K,V> right;
  10. TreeNode<K,V> prev; // needed to unlink next upon deletion
  11. boolean red;
  12. TreeNode(int hash, K key, V val, Node<K,V> next) {
  13. super(hash, key, val, next);
  14. }
  15. /**
  16. * Returns root of tree containing thisnode.
  17. */
  18. final TreeNode<K,V> root() {
  19. for (TreeNode<K,V> r = this, p;;) {
  20. if ((p = r.parent) == null)
  21. return r;
  22. r = p;
  23. }
  24. }

它们的继承关系

359c93b80ab440a4a734c36e56ffd947.png

HashMap中的数组初始化

在JDK1.8的HashMap中对于数组的初始化采用的是延迟初始化方 式。通过resize方法实现初始化处理。resize方法既实现数组初始 化,也实现数组扩容处理。

  1. /**
  2. * Initializes or doubles table size. If null, allocates in
  3. * accord with initial capacity target held in field threshold.
  4. * Otherwise, because we are using power-oftwo expansion, the
  5. * elements from each bin must either stay at same index, or move
  6. * with a power of two offset in the new table.
  7. *
  8. * @return the table
  9. */
  10. final Node<K,V>[] resize() {
  11. Node<K,V>[] oldTab = table;
  12. int oldCap = (oldTab == null) ? 0 : oldTab.length;
  13. int oldThr = threshold;
  14. int newCap, newThr = 0;
  15. if (oldCap > 0) {
  16. if (oldCap >= MAXIMUM_CAPACITY) {
  17. threshold = Integer.MAX_VALUE;
  18. return oldTab;
  19. }
  20. else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
  21. oldCap >= DEFAULT_INITIAL_CAPACITY)
  22. newThr = oldThr << 1; // double threshold
  23. }
  24. else if (oldThr > 0) // initial capacity was placed in threshold
  25. newCap = oldThr;
  26. else { // zero initialthreshold signifies using defaults
  27. newCap = DEFAULT_INITIAL_CAPACITY;
  28. newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
  29. }
  30. if (newThr == 0) {
  31. float ft = (float)newCap * loadFactor;
  32. newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
  33. (int)ft : Integer.MAX_VALUE);
  34. }
  35. threshold = newThr;
  36. @SuppressWarnings({"rawtypes","unchecked"})
  37. Node<K,V>[] newTab = (Node<K,V> [])new Node[newCap];
  38. table = newTab;
  39. if (oldTab != null) {
  40. for (int j = 0; j < oldCap; ++j) {
  41. Node<K,V> e;
  42. if ((e = oldTab[j]) != null) {
  43. oldTab[j] = null;
  44. if (e.next == null)
  45. newTab[e.hash & (newCap - 1)] = e;
  46. else if (e instanceof TreeNode)
  47. ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
  48. else { // preserve order
  49. Node<K,V> loHead = null,loTail = null;
  50. Node<K,V> hiHead = null,hiTail = null;
  51. Node<K,V> next;
  52. do {
  53. next = e.next;
  54. if ((e.hash & oldCap) == 0) {
  55. if (loTail ==null)
  56. loHead = e;
  57. else
  58. loTail.next = e;
  59. loTail = e;
  60. }
  61. else {
  62. if (hiTail == null)
  63. hiHead = e;
  64. else
  65. hiTail.next = e;
  66. hiTail = e;
  67. }
  68. } while ((e = next) != null);
  69. if (loTail != null) {
  70. loTail.next = null;
  71. newTab[j] = loHead;
  72. }
  73. if (hiTail != null) {
  74. hiTail.next = null;
  75. newTab[j + oldCap] = hiHead;
  76. }
  77. }
  78. }
  79. }
  80. }
  81. return newTab;
  82. }

HashMap中计算Hash值

1 获得key对象的hashcode

首先调用key对象的hashcode()方法,获得key的hashcode值。

2 根据hashcode计算出hash值(要求在[0, 数组长度-1]区 间)hashcode是一个整数,我们需要将它转化成[0, 数组长度-1] 的范围。我们要求转化后的hash值尽量均匀地分布在[0,数组长 度-1]这个区间,减少“hash冲突”

2.1 一种极端简单和低下的算法是: hash值 = hashcode/hashcode; 也就是说,hash值总是1。意味着,键值对对象都会存储到 数组索引1位置,这样就形成一个非常长的链表。相当于每存 储一个对象都会发生“hash冲突”,HashMap也退化成了一个 “链表”。

2.2 一种简单和常用的算法是(相除取余算法): hash值 = hashcode%数组长度;

这种算法可以让hash值均匀的分布在[0,数组长度-1]的区间。 但是,这种算法由于使用了“除法”,效率低下。JDK后来改进 了算法。首先约定数组长度必须为2的整数幂,这样采用位运 算即可实现取余的效果:hash值 = hashcode&(数组长 度-1)。

  1. /**
  2. * Associates the specified value with the specified key in this map.
  3. * If the map previously contained a mapping for the key, the old
  4. * value is replaced.
  5. *
  6. * @param key key with which the specified value is to be associated
  7. * @param value value to be associated with the specified key
  8. * @return the previous value associated with <tt>key</tt>, or
  9. * <tt>null</tt> if there was no mapping for <tt>key</tt>.
  10. * (A <tt>null</tt> return can also indicate that the map
  11. * previously associated <tt>null</tt> with <tt>key</tt>.)
  12. */
  13. public V put(K key, V value) {
  14. return putVal(hash(key), key, value, false, true);
  15. }
  16. static final int hash(Object key) {
  17. int h;
  18. return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
  19. }
  20. /**
  21. * Implements Map.put and related methods
  22. *
  23. * @param hash hash for key
  24. * @param key the key
  25. * @param value the value to put
  26. * @param onlyIfAbsent if true, don't change existing value
  27. * @param evict if false, the table is in creation mode.
  28. * @return previous value, or null if none
  29. */
  30. final V putVal(int hash, K key, V value,
  31. boolean onlyIfAbsent,
  32. boolean evict) {
  33. Node<K,V>[] tab; Node<K,V> p; int n, i;
  34. if ((tab = table) == null || (n = tab.length) == 0)
  35. n = (tab = resize()).length;
  36. if ((p = tab[i = (n - 1) & hash]) == null)
  37. tab[i] = newNode(hash, key, value, null);
  38. else {
  39. Node<K,V> e; K k;
  40. if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
  41. e = p;
  42. else if (p instanceof TreeNode)
  43. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  44. else {
  45. for (int binCount = 0; ; ++binCount) {
  46. if ((e = p.next) == null) {
  47. p.next = newNode(hash, key, value, null);
  48. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  49. treeifyBin(tab, hash);
  50. break;
  51. }
  52. if (e.hash == hash &&
  53. ((k = e.key) == key || (key != null && key.equals(k))))
  54. break;
  55. p = e;
  56. }
  57. }
  58. if (e != null) { // existing mapping
  59. for key
  60. V oldValue = e.value;
  61. if (!onlyIfAbsent || oldValue == null)
  62. e.value = value;afterNodeAccess(e);
  63. return oldValue;
  64. }
  65. }
  66. ++modCount;
  67. if (++size > threshold)
  68. resize();
  69. afterNodeInsertion(evict);
  70. return null;
  71. }

HashMap中添加元素

  1. /**
  2. * Associates the specified value with the specified key in this map.
  3. * If the map previously contained a mapping for the key, the old
  4. * value is replaced.
  5. *
  6. * @param key key with which the specified value is to be associated
  7. * @param value value to be associated with the specified key
  8. * @return the previous value associated with <tt>key</tt>, or
  9. * <tt>null</tt> if there was no mapping for <tt>key</tt>.
  10. * (A <tt>null</tt> return can also indicate that the map
  11. * previously associated <tt>null</tt> with <tt>key</tt>.)
  12. */
  13. public V put(K key, V value) {
  14. return putVal(hash(key), key, value,false, true);
  15. }

HashMap中数组扩容

  1. /**
  2. * Implements Map.put and related methods
  3. *
  4. * @param hash hash for key
  5. * @param key the key
  6. * @param value the value to put
  7. * @param onlyIfAbsent if true, don't change
  8. existing value
  9. * @param evict if false, the table is in creation mode.
  10. * @return previous value, or null if none
  11. */
  12. final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
  13. Node<K,V>[] tab; Node<K,V> p; int n, i;
  14. if ((tab = table) == null || (n = tab.length) == 0)
  15. n = (tab = resize()).length;
  16. if ((p = tab[i = (n - 1) & hash]) == null)
  17. tab[i] = newNode(hash, key, value, null);
  18. else {
  19. Node<K,V> e; K k;
  20. if (p.hash == hash &&
  21. ((k = p.key) == key || (key != null && key.equals(k))))
  22. e = p;
  23. else if (p instanceof TreeNode)
  24. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  25. else {
  26. for (int binCount = 0; ; ++binCount) {
  27. if ((e = p.next) == null) {
  28. p.next = newNode(hash, key, value, null);
  29. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  30. treeifyBin(tab,hash);
  31. break;
  32. }
  33. if (e.hash == hash &&
  34. ((k = e.key) == key || (key != null && key.equals(k))))
  35. break;
  36. p = e;
  37. }
  38. }
  39. if (e != null) { // existing mapping
  40. for key
  41. V oldValue = e.value;
  42. if (!onlyIfAbsent || oldValue == null)
  43. e.value = value;
  44. afterNodeAccess(e);
  45. return oldValue;
  46. }
  47. }
  48. ++modCount;
  49. if (++size > threshold)
  50. resize();
  51. afterNodeInsertion(evict);
  52. return null;
  53. }
  54. /**
  55. * Initializes or doubles table size. If null, allocates in
  56. * accord with initial capacity target held in field threshold.
  57. * Otherwise, because we are using power-oftwo expansion, the
  58. * elements from each bin must either stay at same index, or move
  59. * with a power of two offset in the new table.
  60. *
  61. * @return the table
  62. */
  63. final Node<K,V>[] resize() {
  64. Node<K,V>[] oldTab = table;
  65. int oldCap = (oldTab == null) ? 0 : oldTab.length;
  66. int oldThr = threshold;
  67. int newCap, newThr = 0;
  68. if (oldCap > 0) {
  69. if (oldCap >= MAXIMUM_CAPACITY) {
  70. threshold = Integer.MAX_VALUE;
  71. return oldTab;
  72. }
  73. else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
  74. oldCap >= DEFAULT_INITIAL_CAPACITY)
  75. newThr = oldThr << 1; // double threshold
  76. }
  77. else if (oldThr > 0) // initial capacity
  78. was placed in threshold
  79. newCap = oldThr;
  80. else { // zero initial threshold signifies using defaults
  81. newCap = DEFAULT_INITIAL_CAPACITY;
  82. newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
  83. }
  84. if (newThr == 0) {
  85. float ft = (float)newCap * loadFactor;
  86. newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
  87. (int)ft : Integer.MAX_VALUE);
  88. }
  89. threshold = newThr;
  90. @SuppressWarnings({"rawtypes","unchecked"})
  91. Node<K,V>[] newTab = (Node<K,V> [])new Node[newCap];
  92. table = newTab;
  93. if (oldTab != null) {
  94. for (int j = 0; j < oldCap; ++j) {
  95. Node<K,V> e;
  96. if ((e = oldTab[j]) != null) {
  97. oldTab[j] = null;
  98. if (e.next == null)
  99. newTab[e.hash & (newCap - 1)] = e;
  100. else if (e instanceof TreeNode)
  101. ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
  102. else { // preserve order
  103. Node<K,V> loHead = null, loTail = null;
  104. Node<K,V> hiHead = null, hiTail = null;
  105. Node<K,V> next;
  106. do {
  107. next = e.next;
  108. if ((e.hash & oldCap) == 0) {
  109. if (loTail == null)
  110. loHead = e;
  111. else
  112. loTail.next = e;
  113. loTail = e;
  114. }
  115. else {
  116. if (hiTail == null)
  117. hiHead = e;
  118. else
  119. hiTail.next = e;
  120. hiTail = e;
  121. }
  122. } while ((e = next) != null);
  123. if (loTail != null) {
  124. loTail.next = null;
  125. newTab[j] = loHead;
  126. }
  127. if (hiTail != null) {
  128. hiTail.next = null;
  129. newTab[j + oldCap] = hiHead;
  130. }
  131. }
  132. }
  133. }
  134. }
  135. return newTab;
  136. }

TreeMap容器的使用

65388726e944404b99d88a4a0a64ed96.png

TreeMap和HashMap同样实现了Map接口,所以,对于API的用法 来说是没有区别的。HashMap效率高于TreeMap;TreeMap是可 以对键进行排序的一种容器,在需要对键排序时可选用TreeMap。 TreeMap底层是基于红黑树实现的。

在使用TreeMap时需要给定排序规则:

1、元素自身实现比较规则

2、通过比较器实现比较规则

元素自身实现比较规则

  1. public class Users implements
  2. Comparable<Users>{
  3. private String username;
  4. private int userage;
  5. public Users(String username, int userage) {
  6. this.username = username;
  7. this.userage = userage;
  8. }
  9. public Users() { }
  10. @Override
  11. public boolean equals(Object o) {
  12. System.out.println("equals...");
  13. if (this == o) return true;
  14. if (o == null || getClass() != o.getClass()) return false;
  15. Users users = (Users) o;
  16. if (userage != users.userage) return false;
  17. return username != null ? username.equals(users.username) : users.username == null;
  18. }
  19. @Override
  20. public int hashCode() {
  21. int result = username != null ? username.hashCode() : 0;
  22. result = 31 * result + userage;
  23. return result;
  24. }
  25. public String getUsername() {
  26. return username;
  27. }
  28. public void setUsername(String username)
  29. {
  30. this.username = username;
  31. }
  32. public int getUserage() {
  33. return userage;
  34. }
  35. public void setUserage(int userage) {
  36. this.userage = userage;
  37. }
  38. @Override
  39. public String toString() {
  40. return "Users{" +
  41. "username='" + username + '\'' +
  42. ", userage=" + userage +
  43. '}';
  44. }
  45. //定义比较规则
  46. //正数:大,负数:小,0:相等
  47. @Override
  48. public int compareTo(Users o) {
  49. if(this.userage < o.getUserage()){
  50. return 1;
  51. }
  52. if(this.userage == o.getUserage()){
  53. return this.username.compareTo(o.getUsername());
  54. }
  55. return -1;
  56. }
  57. }
  58. public class TreeMapTest {
  59. public static void main(String[] args) {
  60. //实例化TreeMap
  61. Map<Users,String> map = new TreeMap<>();
  62. Users u1 = new Users("oldlu",18);
  63. Users u2 = new Users("admin",22);
  64. Users u3 = new Users("sxt",22);
  65. map.put(u1,"oldlu");
  66. map.put(u2,"admin");
  67. map.put(u3,"sxt");
  68. Set<Users> keys = map.keySet();
  69. for(Users key :keys){
  70. System.out.println(key+" --------- "+map.get(key));
  71. }
  72. }
  73. }

通过比较器实现比较规则

  1. public class Student {
  2. private String name;
  3. private int age;
  4. public Student(String name, int age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. public Student() { }
  9. @Override
  10. public String toString() {
  11. return "Student{" +
  12. "name='" + name + '\'' +
  13. ", age=" + age +
  14. '}';
  15. }
  16. public String getName() {
  17. return name;
  18. }
  19. public void setName(String name) {
  20. this.name = name;
  21. }
  22. public int getAge() {
  23. return age;
  24. }
  25. public void setAge(int age) {
  26. this.age = age;
  27. }
  28. @Override
  29. public boolean equals(Object o) {
  30. if (this == o) return true;
  31. if (o == null || getClass() != o.getClass()) return false;
  32. Student student = (Student) o;
  33. if (age != student.age) return false;
  34. return name != null ? name.equals(student.name) : student.name == null;
  35. }
  36. @Override
  37. public int hashCode() {
  38. int result = name != null ? name.hashCode() : 0;
  39. result = 31 * result + age;
  40. return result;
  41. }
  42. }
  43. public class StudentComparator implements Comparator<Student> {
  44. //定义比较规则
  45. @Override
  46. public int compare(Student o1, Student o2) {
  47. if(o1.getAge() > o2.getAge()){
  48. return 1;
  49. }
  50. if(o1.getAge() == o2.getAge()){
  51. return
  52. o1.getName().compareTo(o2.getName());
  53. }
  54. return -1;
  55. }
  56. }
  57. public class TreeMapTest {
  58. public static void main(String[] args) {
  59. Map<Student,String> treeMap = new TreeMap<>(new StudentComparator());
  60. Student s1 = new Student("oldlu",18);
  61. Student s2 = new Student("admin",22);
  62. Student s3 = new Student("sxt",22);
  63. treeMap.put(s1,"oldlu");
  64. treeMap.put(s2,"admin");
  65. treeMap.put(s3,"sxt");
  66. Set<Student> keys1 = treeMap.keySet();
  67. for(Student key :keys1){
  68. System.out.println(key+" ----"+treeMap.get(key));
  69. }
  70. }
  71. }

TreeMap的底层源码分析

TreeMap是红黑二叉树的典型实现。我们打开TreeMap的源码,发 现里面有一行核心代码:

  1. private transient Entry<K,V> root = null;

root用来存储整个树的根节点。我们继续跟踪Entry(是TreeMap的 内部类)的代码:

85990425bd67454f8ed1a8a42a9af398.png

可以看到里面存储了本身数据、左节点、右节点、父节点、以及节 点颜色。 TreeMap的put()/remove()方法大量使用了红黑树的理 论。在本节课中,不再展开。需要了解更深入的,可以参考专门的 数据结构书籍。 TreeMap和HashMap实现了同样的接口Map,因此,用法对于调 用者来说没有区别。HashMap效率高于TreeMap;在需要排序的 Map时才选用TreeMap。

Iterator接口

528c0c744398468e9d9e1e4ae385d4a3.png

Iterator迭代器接口介绍

Collection接口继承了Iterable接口,在该接口中包含一个名为 iterator的抽象方法,所有实现了Collection接口的容器类对该方法 做了具体实现。iterator方法会返回一个Iterator接口类型的迭代器 对象,在该对象中包含了三个方法用于实现对单例容器的迭代处 理。

Iterator对象的工作原理

1c231ef70adc4b0b92721039c2d32407.png

Iterator接口定义了如下方法:

1 boolean hasNext(); //判断游标当前位置的下一个位置是否还有元素没有被遍历;

2 Object next(); //返回游标当前位置的下一个元素并将游标移动到下一个位置;

3 void remove(); //删除游标当前位置的元素,在执行完next后该操作只能执行一次;

Iterator迭代器的使用

迭代List接口类型容器

  1. public class IteratorListTest {
  2. public static void main(String[] args) {
  3. //实例化容器
  4. List<String> list = new ArrayList<>();
  5. list.add("a");
  6. list.add("b");
  7. list.add("c");
  8. //获取元素
  9. //获取迭代器对象
  10. Iterator<String> iterator = list.iterator();
  11. //方式一:在迭代器中,通过while循环获取元素
  12. while(iterator.hasNext()){
  13. String value = iterator.next();
  14. System.out.println(value);
  15. }
  16. System.out.println("-------------------------------");
  17. //方法二:在迭代器中,通过for循环获取元素
  18. for(Iterator<String> it = list.iterator();it.hasNext();){
  19. String value = it.next();
  20. System.out.println(value);
  21. }
  22. }
  23. }

迭代Set接口类型容器

  1. public class IteratorSetTest {
  2. public static void main(String[] args) {
  3. //实例化Set类型的容器
  4. Set<String> set = new HashSet<>();
  5. set.add("a");
  6. set.add("b");
  7. set.add("c");
  8. //方式一:通过while循环
  9. //获取迭代器对象
  10. Iterator<String> iterator = set.iterator();
  11. while(iterator.hasNext()){
  12. String value = iterator.next();
  13. System.out.println(value);
  14. }
  15. System.out.println("-------------------------");
  16. //方式二:通过for循环
  17. for(Iterator<String> it = set.iterator();it.hasNext();){
  18. String value = it.next();
  19. System.out.println(value);
  20. }
  21. }
  22. }

迭代Map接口类型容器

  1. public class IteratorMapTest {
  2. public static void main(String[] args) {
  3. //实例化HashMap容器
  4. Map<String, String> map = new HashMap<String, String>();
  5. //添加元素
  6. map.put("a", "A");
  7. map.put("b", "B");
  8. map.put("c", "C");
  9. //遍历Map容器方式一
  10. Set<String> keySet = map.keySet();
  11. for (Iterator<String> it = keySet.iterator(); it.hasNext();){
  12. String key = it.next();
  13. String value = map.get(key);
  14. System.out.println(key+" ------------- "+value);
  15. }
  16. System.out.println("------------------------");
  17. //遍历Map容器方式二
  18. Set<Map.Entry<String, String>> entrySet = map.entrySet();
  19. Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
  20. while(iterator.hasNext()){
  21. Map.Entry entry = iterator.next();
  22. System.out.println(entry.getKey()+" ------------ "+ entry.getValue());
  23. }
  24. }
  25. }

在迭代器中删除元素

  1. public class IteratorRemoveTest {
  2. public static void main(String[] args) {
  3. List<String> list = new ArrayList<>();
  4. list.add("a");
  5. list.add("b");
  6. list.add("c");
  7. list.add("d");
  8. Iterator<String> iterator = list.iterator();
  9. while(iterator.hasNext()){
  10. //不要在一次循环中多次调用next方法。
  11. String value = iterator.next();
  12. iterator.remove();
  13. }
  14. System.out.println("----------------");
  15. for(Iterator<String> it = list.iterator();
  16. it.hasNext();){
  17. System.out.println(it.next());
  18. list.add("dddd");
  19. }
  20. }
  21. }

遍历集合的方法总结

遍历List方法一:普通for循环

  1. for(int i=0;i<list.size();i++){//list为集合的对象名
  2. String temp = (String)list.get(i);
  3. System.out.println(temp);
  4. }

遍历List方法二:增强for循环(使用泛型!)

  1. for (String temp : list) {
  2. System.out.println(temp);
  3. }

遍历List方法三:使用Iterator迭代器(1)

  1. for(Iterator iter=
  2. list.iterator();iter.hasNext();){
  3. String temp = (String)iter.next();
  4. System.out.println(temp);
  5. }

遍历List方法四:使用Iterator迭代器(2)

  1. Iterator iter =list.iterator();
  2. while(iter.hasNext()){
  3. Object obj = iter.next();
  4. iter.remove();//如果要遍历时,删除集合中的元素,建议使用这种方式!
  5. System.out.println(obj);
  6. }

遍历Set方法一:增强for循环

  1. for(String temp:set){
  2. System.out.println(temp);
  3. }

遍历Set方法二:使用Iterator迭代器

  1. for(Iterator iter =
  2. set.iterator();iter.hasNext();){
  3. String temp = (String)iter.next();
  4. System.out.println(temp);
  5. }

遍历Map方法一:根据key获取value

  1. Map<Integer, Man> maps = new HashMap<Integer,
  2. Man>();
  3. Set<Integer> keySet = maps.keySet();
  4. for(Integer id : keySet){
  5. System.out.println(maps.get(id).name);
  6. }

遍历Map方法二:使用entrySet

  1. Set<Map.Entry<Integer, Man>> ss =
  2. maps.entrySet();
  3. for (Iterator<Map.Entry<Integer, Man>>
  4. iterator = ss.iterator();
  5. iterator.hasNext();) {
  6. Map.Entry e = iterator.next();
  7. System.out.println(e.getKey()+"--
  8. "+e.getValue());
  9. }

Collections工具类

e1852d9363eb4820acdbfb41ae16b5e9.png

类 java.util.Collections 提供了对Set、List、Map进行排序、填充、 查找元素的辅助方法。

c3c1e983912140cb844418389db2cf2b.png

Collections工具类的常用方法

  1. public class CollectionsTest {
  2. public static void main(String[] args) {
  3. List<String> list = new ArrayList<>();
  4. list.add("c");
  5. list.add("b");
  6. list.add("a");
  7. //对元素排序
  8. Collections.sort(list);
  9. for(String str:list){
  10. System.out.println(str);
  11. }
  12. System.out.println("-------------------");
  13. List<Users> list2 = new ArrayList<>();
  14. Users u = new Users("oldlu",18);
  15. Users u2 = new Users("sxt",22);
  16. Users u3 = new Users("admin",22);
  17. list2.add(u);
  18. list2.add(u2);
  19. list2.add(u3);
  20. //对元素排序
  21. Collections.sort(list2);
  22. for(Users user:list2){
  23. System.out.println(user);
  24. }
  25. System.out.println("-------------------");
  26. List<Student> list3 = new ArrayList<>();
  27. Student s = new Student("oldlu",18);
  28. Student s1 = new Student("sxt",20);
  29. Student s2 = new Student("admin",20);
  30. list3.add(s);
  31. list3.add(s1);
  32. list3.add(s2);
  33. Collections.sort(list3,new StudentComparator());
  34. for(Student student:list3){
  35. System.out.println(student);
  36. }
  37. System.out.println("-------------------");
  38. List<String> list4 = new ArrayList<>();
  39. list4.add("a");
  40. list4.add("b");
  41. list4.add("c");
  42. list4.add("d");
  43. //洗牌
  44. Collections.shuffle(list4);
  45. for(String str:list4){
  46. System.out.println(str);
  47. }
  48. }
  49. }

发表评论

表情:
评论列表 (有 0 条评论,41人围观)

还没有评论,来说两句吧...

相关阅读