一、 哈希表

1. 哈希表的结构特点

hashtable(散列表)
特点:快
结构:顺序表+链表
主结构:顺序表,在每个顺序表的节点再单独引出一个链表。

2. hashCode和equals作用:

hashCode()计算哈希码,是一个整数,根据哈希码可以计算出在哈希表中的数据的位置。
equals()添加时出现了冲突,需要通过equals进行比较,判断是否相同;查询时也需要使用equals进行比较,判断是否相同。

3. 各种类型的数据获取hashCode的方式:

int取自身,看Integer的源码
double同上,看Double的源码
String 将各个字符串的Acall码值按照权重相加

4. 装填因子

装填因子=表中的记录数/哈希表的长度
如果装填因子越小,表明表中还有很多的空单元,则添加发生冲突的可能性就越小;而装填因子越大,则发生冲突的可能性就越大,在查找所耗费的时间就越多。当装填因子在0.5的时候,Hash性能能够达到最优。
因此,一般情况下,装填因子取经验值0.5

Map集合的基本使用
Map(接口)extends Object
子类: HashMap TreeMap LinkedHashMap
特点:key 是唯一的 Value是可以重复的
常用方法:
put();放入键值对
set();取出键
get();获取键值对
clear();集合清除
remove();元素移除
replace();元素替换操作
containsKey();判断当前集合中是否包含我们指定key 的键值对
containValue();判断当前集合中是否包含我们指定value的键值对
isEmpty();判断当前集合是否为空

new对象 分配了内存空间,值为空,是绝对的空,是一种有值(值 = 空)
“” 分配了内存空间,值为空字符串,是相对的空,是一种有值(值 = 空字串)
null 是未分配内存空间,无值,是一种无值(值不存在)

size();返回键值对的个数

  1. import java.util.*;
  2. public class HashMapTest {
  3. public static void main(String[] args) {
  4. Map<Integer,String> map=new HashMap<>();
  5. Map<Integer,String> map1=new TreeMap<>();
  6. Map<Integer,String> map2=new LinkedHashMap<>();
  7. map.put(111,"sxt");//存入元素
  8. map.put(112,"bjsxt");
  9. map.put(113,"sxtbjsxt");
  10. String a=map.get(112);//获取键为112的值赋值给a
  11. Set<Integer> set=map.keySet();//将map中的key保存到集合set中
  12. for (Integer i:set) { //遍历map集合
  13. System.out.println(i+"---------"+map.get(i));
  14. }
  15. System.out.println(set);
  16. map.remove(111);//移除
  17. map.replace(112,"bjsxtsxtsxt");//替换
  18. boolean flag=map.isEmpty();//判断当前集合是否为空
  19. boolean flag1=map.containsKey(113); //是否包含指定键
  20. boolean flag2=map.containsValue("sxt");
  21. int s=map.size();//键值对个数
  22. map.clear();//清空集合
  23. }
  24. }

二、Map

1. HashMap

• JDK1.7及其之前,HashMap底层是一个table数组+链表实现的哈希表存储结构
image.png
• 链表的每个节点就是一个Entry,其中包括:键key、值value、键的哈希码hash、执行下一个节点的引用next四部分

• 在JDK1.8中有一些变化,当链表的存储数据个数大于等于8的时候,不再采用链表存储,而采用红黑树存储结构。这么做主要是查询的时间复杂度上,链表为O(n),而红黑树一直是O(logn)。如果冲突多,并且超过8长度小于6 会自动转成链表结构,采用红黑树来提高效率
image.png
前 1.7 是 Entry 结点,1.8 则是Node 结点,其实相差不大,因为都是实现了 Map.Entry (Map 接口中的 Entry 接口)接口,即,实现了 getKey() , getValue() , equals(Object o )和 hashCode() 等方法;

1.1 HashMap:底层是hash表

  1. key是唯一的,如果key是重复的,后者就会把前者覆盖
  2. 允许key是null
  3. Jdk1.7的时候哈希表采用的是 数组+链表的方式实现的
  4. Jdk1.8及之后哈希表采用的是 数组+链表+红黑树的方式实现的

    1.2 基本用法

    ```java import java.util.*;

public class HashMapTest { public static void main(String[] args) { Map map=new HashMap<>(); Map map1=new TreeMap<>(); Map map2=new LinkedHashMap<>(); map.put(111,”sxt”);//存入元素 map.put(112,”bjsxt”); map.put(113,”sxtbjsxt”); String a=map.get(112);//获取键为112的值赋值给a Set set=map.keySet();//将map中的key保存到集合set中 for (Integer i:set) { //遍历map集合 System.out.println(i+”————-“+map.get(i)); } System.out.println(set); map.remove(111);//移除 map.replace(112,”bjsxtsxtsxt”);//替换 boolean flag=map.isEmpty();//判断当前集合是否为空 boolean flag1=map.containsKey(113); //是否包含指定键 boolean flag2=map.containsValue(“sxt”); int s=map.size();//键值对个数 map.clear();//清空集合 } }

  1. <a name="HDz6U"></a>
  2. #### 1.3 HashMap的底层源码jdk1.7
  3. HashMap map=new HashMap();<br />在实例化以后,底层创建了长度是16的一维数组Entry【】table。<br />.........可能已经执行过多次的put......<br />首先,调用key1所在类型的hashcode()计算key1的哈希值,此哈希值经过某种算法计算过以后,得到entry数组中的存放位置。<br />如果此位置上的数据为空,此时的key1-value1添加成功。-----情况1<br />如果此位置上的数据不为空,(意味着此位置上存在一个或者多个数据(以链表形式存在),比较key1和已经存在的一个或者多个数据的hash值):
  4. 1. 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
  5. 1. 如果key1的哈希值已经存在某个数据(key2-value2)的哈希值相同,继续比较;调用key1所在类的equals(key2)。
  6. 如果equals()返回false:此时key1-value1添加成功。----情况3<br />如果equals()返回true:使用value1替换value2<br />补充:关于情况2和情况3,此时key1-value1和原来的数据以链表的方式存储。<br />在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容到原来容量的2倍,并将原有的数据复制过来。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628490540904-a1551c80-fb44-40fe-92d1-594fdff2050c.png#align=left&display=inline&height=996&margin=%5Bobject%20Object%5D&name=image.png&originHeight=996&originWidth=1662&size=104556&status=done&style=none&width=1662)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628490582095-751a8ffb-15d5-41a8-a4c1-3272ba77a5c9.png#align=left&display=inline&height=1207&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1207&originWidth=1654&size=177046&status=done&style=none&width=1654)<br />面试题A<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628495365247-4a6806dd-a0b2-4da1-b38d-929a7e9669f9.png#align=left&display=inline&height=735&margin=%5Bobject%20Object%5D&name=image.png&originHeight=735&originWidth=1568&size=78413&status=done&style=none&width=1568)<br />----------------------------------------------------------------------------
  7. <a name="l09Qh"></a>
  8. #### 1.4 HashMap的底层源码jdk1.8
  9. jkd8相较于jdk7在底层实现方面的不同:
  10. 1. new HashMap();底层没有创建一个长度为16的数组
  11. 1. jdk8底层的数组是:Node【】,而非Entry【】
  12. 1. 首次调用put()方法的时候,底层创建长度为16的数组
  13. 1. jdk7底层结构只有:数组+链表;jdk8中底层结构:数组+链表+红黑树。当数组的某一个索引位置上的元素以链表形式存在的数据个数>8,且当前数组的长度>64时,此时此索引位置上所有数据改为使用红黑树存储。
  14. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628495411936-d4fd0593-d827-4e3f-ac54-dfb7b877689a.png#align=left&display=inline&height=720&margin=%5Bobject%20Object%5D&name=image.png&originHeight=720&originWidth=1649&size=71570&status=done&style=none&width=1649)<br />-------------------------------------------------------------------------------<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628495427031-b6fe3261-9299-424a-a52d-56c2b6d213ca.png#align=left&display=inline&height=1394&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1394&originWidth=1661&size=167175&status=done&style=none&width=1661)
  15. <a name="nCxj0"></a>
  16. ### 2. TreeMap
  17. <a name="ouzM1"></a>
  18. #### 2.1 基本特征
  19. • 基本特征:二叉树、二叉查找树、二叉平衡树、红黑树
  20. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628592295328-bcb6d70c-92cf-4033-b6ac-01911b8adf26.png#align=left&display=inline&height=157&margin=%5Bobject%20Object%5D&name=image.png&originHeight=157&originWidth=202&size=13994&status=done&style=none&width=202) <br />• 每个节点的结构<br />`![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628592306565-0d23b112-c286-4f52-8854-cd6dd19f9428.png#align=left&display=inline&height=47&margin=%5Bobject%20Object%5D&name=image.png&originHeight=47&originWidth=350&size=8900&status=done&style=none&width=350)`<br />TreeMap:底层是Tree(红黑树)
  21. 1. key是唯一的,如果key是重复的,后者就会把前者覆盖
  22. 1. 不允许key为null
  23. 红黑树在新增节点过程中比较复杂,复杂归复杂它同样必须要依据上面提到的五点规范
  24. <a name="04qMQ"></a>
  25. #### 2.2 红黑树的特点
  26. [1]每个节点都只能是红色或者黑色。<br />[2]根节点是黑色。<br />[3]每个叶节点(NIL节点,NULL空节点)是黑色的。<br />[4]每个红色节点的两个子节点都是黑色 (****从每个叶子到根的路径上不会有两个连续的红色节点) 。<br />[5]从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。<br /> 由于规则1、2、3基本都会满足,下面我们主要讨论规则4、5。
  27. <a name="38euE"></a>
  28. #### 2.3 红黑树插入变换
  29. 假设我们这里有一棵最简单的树,我们规定新增的节点为N、它的父节点为P、P的兄弟节点为U、P的父节点为G。 <br />对于新节点的插入有如下三个关键地方:<br />1、插入新节点总是红色节点。<br />2、如果插入节点的父节点是黑色,能维持性质 。<br />3、如果插入节点的父节点是红色,破坏了性质。<br />故插入算法就是通过重新着色或旋转,来维持性质,可能出现的情况如下:<br /> <br />【情况一】为跟节点<br /> <br />若新插入的节点N没有父节点,则直接当做根据节点插入即可,同时将颜色设置为黑色。<br /> <br />【情况二】父结点为黑色<br /> <br />那么插入的红色节点将不会影响红黑树的平衡,直接插入即可。<br /> <br />【情况三】父节点和叔节点都为红色<br /> <br />当叔父结点为红色时,无需进行旋转操作,只要将父和叔结点变为黑色,将祖父结点变为红色即可。<br /> <br />但是经过上面的处理,可能G节点的父节点也是红色,这个时候我们需要将G节点当做新增节点递归处理。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628592559263-a4d11e8c-4976-4d0f-bcd9-b04ad669da90.png#align=left&display=inline&height=161&margin=%5Bobject%20Object%5D&name=image.png&originHeight=161&originWidth=373&size=23439&status=done&style=none&width=373)<br /> <br />【情况四】父红,叔黑,并且新增节点和父节点都为左子树<br /> <br />对于这种情况先已P节点为中心进行右旋转,在旋转后产生的树中,节点P是节点N、G的父节点。<br />但是这棵树并不规范,所以我们将P、G节点的颜色进行交换,使之其满足规范。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628592581984-ab97c189-a2ce-4976-a3fc-2a9ed8c83670.png#align=left&display=inline&height=138&margin=%5Bobject%20Object%5D&name=image.png&originHeight=138&originWidth=483&size=27849&status=done&style=none&width=483)<br />【情况五】父红,叔黑,并且新增节点和父节点都为右子树<br /> <br />对于这种情况先已P节点为中心进行左旋转,在旋转后产生的树中,节点P是节点G、N的父节点。但是这棵树并不规范,所以我们将P、G节点的颜色进行交换,使之其满足规范。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628592625000-c8d71a45-63f4-4abd-a88a-ee8522d298ea.png#align=left&display=inline&height=162&margin=%5Bobject%20Object%5D&name=image.png&originHeight=162&originWidth=554&size=34310&status=done&style=none&width=554)<br />【情况六】父红,叔黑,并且新增节点为左子树,父节点为右子树<br /> <br />对于这种情况先以N节点为中心进行右旋转,在旋转后产生的树中,节点N是节点P、X的父节点。然后再以N节点为中心进行左旋转,在旋转后产生的树中,节点N是节点P、G的父节点。但是这棵树并不规范,所以我们将N、G节点的颜色进行交换,使之其满足规范。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628592638436-975ffb82-8b5b-4064-a504-f2d2f3ccad87.png#align=left&display=inline&height=209&margin=%5Bobject%20Object%5D&name=image.png&originHeight=209&originWidth=374&size=31043&status=done&style=none&width=374)<br /> <br />【情况七】父红,叔黑,并且新增节点为右子树,父节点为左子树<br /> <br />对于这种情况先以N节点为中心进行左旋转,在旋转后产生的树中,节点N是节点P、Y的父节点。然后再以N节点为中心进行右旋转,在旋转后产生的树中,节点N是节点P、G的父节点。但是这棵树并不规范,所以我们将N、G节点的颜色进行交换,使之其满足规范。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628592648845-ba0907fe-44dd-4a2f-a5e7-7ca80b5001c4.png#align=left&display=inline&height=261&margin=%5Bobject%20Object%5D&name=image.png&originHeight=261&originWidth=393&size=43469&status=done&style=none&width=393)
  30. <a name="jyJSD"></a>
  31. ### 3. LinkedHashMap
  32. <a name="wq481"></a>
  33. ## 三、 迭代器iteration
  34. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22310212/1628575026979-bfa1a392-7332-422f-9a20-be2a740dce8a.png#align=left&display=inline&height=810&margin=%5Bobject%20Object%5D&name=image.png&originHeight=810&originWidth=1672&size=104083&status=done&style=none&width=1672)
  35. <a name="VcupA"></a>
  36. ### 1. 迭代器在List、Set、Map中的应用
  37. ```java
  38. import java.util.*;
  39. public class IteratorTestA {
  40. public static void main(String[] args) {
  41. System.out.println("----------------List Iterator--------------");
  42. List<String> list=new ArrayList<>();
  43. list.add("A");
  44. list.add("B");
  45. list.add("C");
  46. list.add("D");
  47. Iterator<String> iterator = list.iterator();
  48. while (iterator.hasNext()){
  49. String next = iterator.next();
  50. System.out.println(next);
  51. }
  52. System.out.println("----------------Set Iterator--------------");
  53. Set<String> set=new HashSet<>();
  54. set.add("A");
  55. set.add("B");
  56. set.add("C");
  57. set.add("D");
  58. Iterator<String> iterator1 = set.iterator();
  59. while (iterator1.hasNext()){
  60. String next1 = iterator1.next();
  61. System.out.println(next1);
  62. }
  63. System.out.println("----------------Map Iterator--------------");
  64. Map<String,String> map=new HashMap<>();
  65. map.put("A","bj");
  66. map.put("B","sxt");
  67. map.put("C","bjsxt");
  68. Iterator<String> iterator2 = map.keySet().iterator();
  69. while (iterator2.hasNext()){
  70. String next2 = iterator2.next();
  71. System.out.println(map.get(next2));
  72. }
  73. }

2. 针对List集合的迭代器

  1. import java.util.ArrayList;
  2. import java.util.Iterator;
  3. import java.util.List;
  4. import java.util.ListIterator;
  5. public class IteratorTest {
  6. public static void main(String[] args) {
  7. List<String> list=new ArrayList<>();
  8. list.add("A");
  9. list.add("B");
  10. list.add("C");
  11. list.add("D");
  12. System.out.println("-----------------iterator遍历----------------------");
  13. Iterator<String> iterator = list.iterator();
  14. while (iterator.hasNext()){ //判断数组中是否有下一个值
  15. String next = iterator.next(); //保存值到next中
  16. System.out.println(next);
  17. }
  18. System.out.println("-----------------只是针对list集合的迭代器----------------------");
  19. ListIterator<String> stringListIterator = list.listIterator();//正序遍历
  20. while (stringListIterator.hasNext()){
  21. String next = stringListIterator.next();
  22. System.out.println(next);
  23. }
  24. System.out.println("-------------------------------------------------------");
  25. while (stringListIterator.hasPrevious()){
  26. String previous = stringListIterator.previous();//逆序遍历
  27. System.out.println(previous);
  28. }
  29. }
  30. }

四、 CollectionS工具类

CollectionS工具类中的常用方法
list集合的线程是不安全的,我们使用sysnchronizedList使集合变得安全

  1. public class CollectionTest {
  2. public static void main(String[] args) {
  3. List<Integer> list=new ArrayList<>();
  4. Collections.addAll(list,20,12,13,14,15,16,17,18,19); //在指定集合中快速的追加元素
  5. System.out.println(list);
  6. Collections.sort(list); //对集合中的元素进行排序
  7. System.out.println(list);
  8. int i = Collections.binarySearch(list, 15); //使用二分查找法对查询元素对应的下标,要求必须集合是有序的
  9. System.out.println(i);
  10. Integer min=Collections.min(list); //最小值
  11. Integer max=Collections.max(list);//最大值
  12. System.out.println(min+"-----"+max);
  13. //Collections.fill(list,null);//快速填充元素
  14. List<Integer> list1=new ArrayList<>();
  15. Collections.addAll(list1,0,0,0,0,0,0,0,0,0,0,0,0);
  16. Collections.copy(list1,list);//集合赋值,保证原集合和目标集合的长度一样
  17. System.out.println(list1);
  18. //list集合的线程是不安全的,我们使用sysnchronizedList使集合变得安全
  19. List<Integer> list2=Collections.synchronizedList(list);//线程安全
  20. System.out.println(list2);
  21. /* Collections.synchronizedMap();
  22. Collections.synchronizedSet();*/
  23. }

五、集合分代

1. 第一代

Vector

• 实现原理和ArrayList相同,功能相同,都是长度可变的数组结构,很多情况下可以互用
• 两者的主要区别如下
• Vector是早期JDK接口,ArrayList是替代Vector的新接口
• Vector线程安全,效率低下;ArrayList重速度轻安全,线程非安全
• 长度需增长时,Vector默认增长一倍,ArrayList增长50%

Hashtable类

• 实现原理和HashMap相同,功能相同,底层都是哈希表结构,查询速度快,很多情况下可互用
• 两者的主要区别如下
• Hashtable是早期JDK提供,HashMap是新版JDK提供
• Hashtable继承Dictionary类,HashMap实现Map接口
• Hashtable线程安全,HashMap线程非安全
• Hashtable不允许key的null值,HashMap允许null值

2. 第二代

Lsit Set Map

3. 第三代

ConcurrentHashMap

CopyOnWriteArrayList

CopyOnWriteArraySet:

六、 泛型

没有用泛型之前:

  • 不安全:添加元素是无检查,宽进
  • 繁琐:获取元素时需要强制类型转换 严出

采用泛型之后:

  • 安全 严进
  • 简单 宽出
  1. 泛型分类

    1. 泛型类

    1. public class ArrayList<E> {
    2. }

    2. 泛型接口

    1. public interface List<E> {
    2. }

    3. 泛型方法

    E:element 元素
    T:type 类型
    K:key 键
    V:value:值

    1. public <T> void cc(T t){ }
    2. public static <T> void dd(T t){ }

    4. 上限和下限通配符

    ```java public class Test02 { public static void sxt(List list){} //? 传入类型只可以是UU的父类/接口 下限通配符 public static void sxt1(List<? super UU> list){} //? 传入类型只可以使UU的本类/子类 上限通配符 public static void sxt2(List<? extends UU> list){}

    public static void main(String[] args) {

    1. sxt(new ArrayList<UU>());
    2. sxt1(new ArrayList<TT>());
    3. sxt2(new ArrayList<RR>());

    } } class TT{} class UU extends TT{} class RR extends UU{}

```