集合

l  Collection 层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 提供更具体的子接口(如 Set 和 List、Queue)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。

l  List:有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

l  Queue:队列通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素。不过优先级队列和 LIFO 队列(或堆栈)例外,前者根据提供的比较器或元素的自然顺序对元素进行排序,后者按 LIFO(后进先出)的方式对元素进行排序。

l  Set:一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。正如其名称所暗示的,此接口模仿了数学上的 set 抽象。

l  SortedSet进一步提供关于元素的总体排序 的 Set。这些元素使用其自然顺序进行排序,或者根据通常在创建有序 set 时提供的 Comparator进行排序。该 set 的迭代器将按元素升序遍历 set。提供了一些附加的操作来利用这种排序。

l  Map:将键映射到值(key,value)的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。 Map 接口提供三种collection 视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序 定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可明确保证其顺序,如 TreeMap 类;另一些映射实现则不保证顺序,如 HashMap 类。

l  SortedMap进一步提供关于键的总体排序 的 Map。该映射是根据其键的自然顺序进行排序的,或者根据通常在创建有序映射时提供的 Comparator 进行排序。

数组是一个容器,已经可以存储多个对象,逻辑结构是线性的,顺序的存储结构;

申请内存:一次申请一大段连续的空间,一旦申请到了,内存就固定了。

存储特点:所有数据存储在这个连续的空间中,数组中的每一个元素都是一个具体的数据(或对象),所有数据都紧密排布,不能有间隔。

  1. 操作
  2. 查询:每一个元素都有一个数值下标,可以通过下标瞬间定位到某个元素
  3. 增加:
  4. 先使用total变量辅助记录实际存储元素个数
  5. 从尾部增加:数组名[total++]=新元素
  6. 从其他位置插入:先把index位置开始所有元素后移,然后数组名 [index]=新元素
  7. 删除:先把index后面的元素前移,然后数组名[total--]=null
  8. 改:直接数组名[index]=新元素

  优点:按索引查询效率高

但是数组有一些不足的地方:(添加/ 删除效率低,都涉及到移动元素;
* (1)一旦长度确定就没法修改,如果要修改,程序要负责扩容代码。
* (2)无法直接获取实际存储了几个对象,需要类似于total的变量辅助

集合:新的容器,只用来装对象,不能用来装基本数据类型的数据
* 集合比数组
* (1)类型更丰富,它有各种特征的集合
* (2)无需程序员来编写“扩容”等代码,也不用通过"total"来记录实际的元素的个数
*
 分为两大类:
  (1)Collection:存储一组对象,单身party
  (2)Map:存储“映射关系”的,存储“键值对” (key,value),情侣party,家庭party

* Collection:最常用的两个子接口
   List:有序(添加的顺序)的可重复的
   Set:无序(添加的顺序)的不可重复

1、Collection接口

  1. java.util.Collection:接口
  2.   JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set List)实现
  3.   
  4. 1、添加
  5. * boolean add(Object obj)
  6. * boolean addAll(Collection other):
  7. * 当前集合 = 当前集合 other集合
  8. Collection c = new ArrayList(); //多态引用; ArrayList是List接口的实现类,List接口是Collection的子接口
  9. c.add( 对象 ); c.add(12);    //因为12会自动装箱类Integer的对象
  10. c1.add(c2);             //增加另外一个集合; 如果有重复元素,也会添加进去。集合可以有重复元素;
  11. c.add(c2);//把C2当成一个整体看; c.addAll(c2);//把c2中的元素,一个一个添加到c中
  12. 2、删除
  13. * boolean remove(Object o)
  14. * boolean removeAll(Collection other)
  15. * 当前集合 = 当前集合 - (当前集合 other)
  16. * void clear()
  17. c.remove( );
  18. //c.removeAll(c2);//会用c2中的元素,与c中的元素进行equals,如果相同的就把c集合中的元素删除,而c2中的元素不会改变不会被删除。如果移除成功就返回true,否则false
  19. 3、修改(没有提供)
  20. 4、查找:
  21. * boolean contains(Object o)
  22. * boolean containsAll(Collection c); c1.containsAll(c2); 如果c1中的元素全部都包含c2的元素,就返回true c2是否是c1的子集。
  23. * 判断c集合是否是当前集合的子集
  24. * boolean isEmpty()

  删除、查找等要依据元素的equals方法,如果元素没有重写equals,那么equals就等同于==,要注意元素是否重写equals。

  像String,Integer...等重写了equals


  1. 5、获取元素的个数
  2. * int size()
  3. 6、排序(没有提供)
  4. * 没有提供的原因,某些集合自带排序功能,而且Collections工具类有排序功能
  5. *
  6. 7、遍历
  7. * Iterator<E> iterator() :获取集合自身的迭代器,用于遍历集合用的
  8. * Object[] toArray()
  9. *
  10.     //用迭代器迭代集合;用于按条件删除或修改等; Iterator接口
  11. Iterator iter = c1.iterator(); Aboolean hasNext():是否还有元素可迭代
  12. Object obj = iter.next();          B:Object next() 取出下一个元素  
  13. for (Object object : c1) { Cremove():删除刚刚迭代的元素
  14. System.out.print(object);
  15. }
  16. //转成数组迭代;
  17. Object[] objArray = c1.toArray();
  18. for (Object object : objArray) {
  19. System.out.print(object);
  20. }
  21. //直接用增强for循环; 只是查找和查看,用它更简洁更快
  22. for (Object object : c1) {
  23. System.out.println(object);
  24. }
  25. 8、求两个集合的交集
  26. * boolean retainAll(Collection<?> c)
  27.    当前集合 = 当前集合 other
  28. c1.retainAll(c2);
  29.   1)如果两个集合元素完全一样 返回值为false2c1包含于c2 返回值为false3)其他 返回值为true
  30.     实际上该方法是指:如果集合c1中的元素都在集合c2中,则c1中的元素不做移除操作,反之如果只要有一个不在c2中则会进行移除操作。
  31.     即:c1进行移除操作返回值为:true; 反之返回值则为false
  32.   boolean retainAll = c1.retainAll(c2);
  33.   System.out.println(retainAll);
  34.   System.out.println("c的有效元素的个数:" + c1.size());//如果存在交集,有相同元素,c1中只保留相同的元素;
  35.   //如果不存在相同元素,则c1会变空;

java.util.Iterator接口:

(1)boolean hasNext() :是否有下一个元素需要迭代
(2)Object next() :取出下一个元素
(3)void remove() :删除刚刚迭代的元素

  1.     Iterator iter = c.iterator();//在iterator()给我们创建了一个Iterator的实现类对象; iter的作用就是遍历、迭代集合;
  2. while(iter.hasNext()){
  3. Object object = iter.next();
  4. Student stu = (Student) object;
  5. if(stu.getScore() < 90){ // if("李四".equals(obj))
  6. iter.remove();//调用的是迭代器自己的删除方法; //而foreach不允许在遍历是添加、删除元素
  7. }
  8. }//要在while循环结束后再迭代查看它
  9. System.out.println("迭代删除后:---------------------");
  10. for (Object object : c) {
  11. System.out.println(object);
  12. }
  1. 一、增加一个接口:java.lang.Iterable接口
  2. * JDK1.5增加
  3. * 它有一个抽象方法:Iterator iterator()
  4. * 实现这个接口允许对象成为 "foreach" 语句的目标
  5. * CollectionJDK1.5之后开始继承Iterable接口。
  6. 二、java.util.Iterator接口在哪里实现的?
  7. * //左边是Iterator接口,说明右边一定创建了一个Iterator接口的实现类对象,否则iterator调用方法是没有方法体可以执行
  8. Iterator iterator = list.iterator();
  9. * 跟踪源代码:list.iterator()
  10. * public Iterator<E> iterator() {
  11. return new Itr();//Itr是一个内部类
  12. }
  13. * 跟踪每一种Collection的集合发现,所有的集合在内部都一个内部类来实现java.util.Iterator接口。
  14. *
  15. * 为什么?
  16. * 1)每一种集合的内部实现物理结构不一样,有的是数组有的是链表等,迭代方式不一样,无法给出统一的实现
  17. * 2)迭代的作用,为某个集合迭代,遍历元素,那么它只为某类集合服务,迭代时又需要访问集合的内部(private)的元素,
  18. * 所以我们设计为内部类更合适。
  19. *
  20. * 我们可以把迭代器比喻成:公交车上的售票员,飞机上空姐,他只为本集合服务
  21. */

2、 java.util.List(比Collection多了和索引index相关的方法)

* 有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。
* 用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
* List是Collection的子接口,那么Collection中的方法它也有。* List增加了Collection接口没有的方法,和索引位置相关的方法:

  1. 1、添加
  2. * boolean add(Object obj):List系列的,默认添加到最后
  3. * boolean addAll(Collection other):List系列的,默认添加到最后
  4. *
  5. * boolean add(int index ,Object obj):在指定位置index处,插入一个元素obj
  6. * boolean add(int index, Collection other):在指定位置index处,插入多个元素,other中的多个元素
  7. *
  8. 2、删除
  9. * boolean remove(Object o) 这个是指定元素。
  10. * boolean removeAll(Collection other)
  11. * void clear()
  12. *
  13. * Object remove(int index):删除指定位置index的元素,删除的同时还返回了该元素,如果需要就接收,不需要就不接收。 //Object remove = list.remove(1);
  14. *
  15. 3、修改(有提供)
  16. * Object set(int index, Object element) :替换集合index位置的元为element,返回被替换的旧元素 lis.set( 1, 8 );
  17. *
  18. 4、查找:
  19. * boolean contains(Object o)
  20. * boolean containsAll(Collection c)
  21. * 判断c集合是否是当前集合的子集
  22. * boolean isEmpty()
  23. *
  24. * int indexOf(Object o) //int index = list.indexOf("李四");
  25. * int lastIndexOf(Object o) //查找最后一个元素的索引位置
  26. * Object get(int index) //list.get( 2 ) ;
  27. *
  28. 5、获取元素的个数
  29. * int size()
  30. *
  31. 7、遍历
  32. * Iterator<E> iterator() :获取集合自身的迭代器,用于遍历集合用的
  33. * Object[] toArray()
  34. * 增加了:
  35. * ListIterator<E> listIterator():
  36. ListIteratorIterator有什么不同?
  37. Iterator只能从前往后遍历,而ListIterator可以从任意位置从前往后,从后往前。
  38. ListIterator iter = lis.listIterator();
  39. while(iter.hasNext()){
  40. Object nex = iter.next();
  41. System.out.println(nex);
  42. }
  43. ListIterator iterator = lis.listIterator(lis.size()); //从后边往前边迭代; int previousIndex();还增加了迭代的同时:set和add
  44. while(iterator.hasPrevious()){
  45. Object pre = iterator.previous();
  46. System.out.println(pre);
  47. }
  48. 8、截取列表的一部分
  49. List<E> subList(int fromIndex, int toIndex)
  50. System.out.println(lis.subList(0, 3)); //[1, 2, 3]

List接口的实现类

  1. java.util.List接口的实现类有:
  2. * java.util.ArrayList:动态数组
  3. * java.util.Vector:动态数组
  4. * java.util.LinkedList:链表
  5. * java.util.Stack:栈
  6. ArrayListVector:(底层都是数组--称为动态数组)
  7. * Vector:是旧版本,线程安全的,扩容机制为原来的倍,支持旧版的Enumeration迭代器
  8. * ArrayList:是新版本,线程不安全的,扩容机制为原来的1.5倍,不支持Enumeration迭代器
  9. * ArrayList只用:foreachIteratorListIterator
  10. * 只看,不改:foreach
  11. * 看,并且删除:从前往后:Iterator
  12. * 看,并且删除、插入,从前往后,或从后往前:ListIterator

  如果需要再细化:
   ArrayList在JDK1.7之后,如果new ArrayList(),一开始初始化为一个长度为0的空数组,在第一次添加元素时,初始化为容量为10的数组。
   Vector:new Vector()直接初始化为容量为10的数组。


  1. 动态数组与链表LinkedList区别:底层的物理结构不同:
  2.   动态数组底层的实现是数组;
  3. * 优点:可以根据索引快速的遍历和查找元素
  4. * 缺点:长度不够,需要扩容,影响性能
  5. * 删除后,要移动元素,并且空出大量的空间,浪费空间
  6. * 需要开辟连续的存储空间
  7.   LinkedList是链表结构。
  8. * 优点:有几个元素,占几个空间,不需要连续的空间
  9. * 添加时,比较快,不需要扩容
  10. * 删除:删除时,比较快,不需要移动元素;只要修改前后元素的关系就可以,和其他元素无关,无需移动
  11. 缺点:无法索引快速定位,如果要按索引操作,效率比较低,从头遍历

 ③ Stack:栈,一种“先进后出FILO”或“后进先出LIFO”数据结构的集合。

  Stack s = new Stack();

  (1)push(Object obj)             s.push("张三");    
  (2)Object peek():查看栈顶元素是什么    Object peek = s.peek();
  (3)Object pop():取出栈顶元素
  
 LinkedList:双向链表(即记录第一个结点first,又记录最后一个结点last)
   队列Queue:先进先出(FIFO)
   双端队列Deque:就可以从队头出去,也可以从队尾出去
   从JDK1.6开始LinkedList又实现了Deque接口。
   LinkedList把栈、队列、双向链表、双端队列所有方法都集为一身,只要调用对应的方法就可以实现各种需求。
  

自己设计一个MyArrayList,类似于ArrayList
MyArrayList对象作为容器使用:

添加、获取有效元素的个数、根据索引删除; 从索引位置,把右边的元素移动到左边;根据对象删除;根据对象查找下标;返回数组中实际存储的元素;判断某个元素是否存在

在指定位置添加,从index位置起它后边的元素都往右边移动;替换指定位置的元素,并返回被替换的旧元素;返回指定位置的元素

  1. import java.util.Arrays;
  2.  
  3. public class MyArrayList {
  4.  
  5. private Object[] arr;
  6. private int total;
  7.  
  8. public MyArrayList() {
  9. super();
  10. //给数组初始化;
  11. arr = new Object[10];
  12. }
  13.  
  14. public MyArrayList(Object[] arr, int total) {
  15. super();
  16. this.arr = arr;
  17. this.total = total;
  18. }
  19.  
  20. //1. 添加方法
  21. public void add(Object obj){
  22.  
  23. if(total == arr.length){ //判断数组是否满了,满了就扩容
  24. //扩容
  25. arr = Arrays.copyOf(arr, arr.length * 2);
  26. }
  27. arr[total++] = obj;
  28.  
  29. }
  30.  
  31. //2.获取有效元素的个数;
  32. public int size(){
  33. return total;
  34. }
  35.  
  36. //3.根据索引删除; 从索引位置,把右边的元素移动到左边;
  37. public void remove(int index){
  38. if(index < 0 || index > total){
  39. throw new IndexOutOfBoundsException("下标越界了");
  40. }
  41. // 1,2,3,4,5,6,null ;要把index=1的小标删除,从index+1的位置移动,需要移动3,4,5,6
  42. System.arraycopy(arr, index+1, arr, index, total-index-1);
  43. arr[total-1] = null;
  44. total--;
  45. }
  46. //4.根据对象删除
  47. public void remove(Object obj){
  48. int index = indexOf(obj);//调用找下标的方法
  49.  
  50. if(index != -1){
  51. remove(index);
  52. }
  53. }
  54.  
  55. //5.根据对象查找下标
  56. public int indexOf(Object obj){
  57. int index = -1;
  58. if(obj == null){
  59. for(int i = 0; i < total; i++){
  60. if(obj == arr[i]){
  61. index = i;
  62. break;
  63. }
  64. }
  65. }else{
  66. for(int i = 0; i < total; i++){
  67. if(obj.equals(arr[i])){
  68. index = i;
  69. break;
  70. }
  71. }
  72. }
  73. return index;
  74. }
  75.  
  76. //6.返回数组中实际存储的元素
  77. public Object[] toArray(){
  78.  
  79. return Arrays.copyOf(arr, total);
  80.  
  81. }
  82. //7.判断某个元素是否存在
  83. public boolean contains(Object obj){
  84. int index = indexOf(obj);
  85. return index != -1;
  86. }
  87.  
  88. //8.在指定位置添加,从index位置起它后边的元素都往右边移动;
  89. public void add(int index, Object obj){
  90. if(index < 0 || index > total ){
  91. throw new IndexOutOfBoundsException("下标越界了");
  92. }
  93. if(total == index){
  94. //扩容
  95. arr = Arrays.copyOf(arr, arr.length * 2);
  96. } // 1,2,3,4,5,null; total=5,在index=2的位置插入一个新的
  97. System.arraycopy(arr, index, arr, index+1, total-index);
  98. arr[index] = obj;
  99. total++;
  100.  
  101. }
  102.  
  103. //9.替换指定位置的元素,并返回被替换的旧元素
  104. public Object set(int index, Object value){
  105. if(index < 0 || index > total ){
  106. throw new IndexOutOfBoundsException("下标越界了");
  107. }
  108. Object old = arr[index];
  109. arr[index] = value;
  110. return old;
  111.  
  112. }
  113.  
  114. //10.返回指定位置的元素
  115. public Object get(int index){
  116. if(index < 0 || index > total ){
  117. throw new IndexOutOfBoundsException("下标越界了");
  118. }
  119. return arr[index];
  120. }
  121.  
  122. }

单链表的添加和删除方法

  1. package com.atguigu.testlink;
  2.  
  3. public class MyLinked { //单链表
  4.  
  5. private Node first; //第一个节点的地址,节点就包含了数据data和next节点;
  6. private int total;
  7.  
  8. class Node{
  9.  
  10. Object data;
  11. Node next;
  12. public Node(Object data, Node next) {
  13. super();
  14. this.data = data;
  15. this.next = next;
  16. }
  17. }
  18. //1、添加方法
  19. public void add(Object obj){
  20. Node newNode = new Node(obj, null); //1)创建新节点newNode,它就包含数据对象和记录下一节点的地址
  21. if(first == null){ //2)如果链表是空的;
  22. first = newNode; //
  23.  
  24. }else{
  25. Node last = first;// 1)先找到最后一个节点的地址
  26. while(last.next != null){
  27. last = last.next;
  28. } //如果为空了循环就退出来了,就把新节点赋值给它
  29. last.next = newNode; //2)然后最后一个节点.next = newNode;
  30. }
  31. total++;
  32. }
  33. //2、获取有效元素个数
  34. public int size(){
  35. return total;
  36. }
  37.  
  38. //返回链表所有的数据
  39. public Object[] toArray(){
  40.  
  41. Object[] all = new Object[total];
  42. //遍历链表,把Node的data放到all[i]中;
  43.  
  44. Node node = first; //
  45. for (int i = 0; i < total; i++) {
  46. all[i] = node.data;
  47. node = node.next;
  48. }
  49. return all;
  50.  
  51. }
  52.  
  53. //4、删除
  54. public void remove(Object obj){
  55. if(first == null){ //1)链表是空的,直接结束
  56. return;
  57. }else{
  58. //2)要删除的就是第一个节点;//判断first是否是被删除目标
  59. boolean isRemove = false;
  60. if(obj == null && first.data == null){ //分为obj是否为空
  61. isRemove = true;
  62. }else if(obj != null && obj.equals(first.data)){
  63. isRemove = true;
  64. }
  65. if(isRemove == true){ //isRemove是true说明第一个结点就是要删除的结点
  66. first = first.next;
  67. }else{ //说明第一个结点不是要删除的目标结点
  68. Node lastNode = first;
  69. Node node = lastNode.next;
  70. Node removeNode = null;
  71. // 遍历查找第二个结点到最后一个结点是否有被删除的目标结点
  72. while(node.next != null){//把条件修改为node!=null,因为node = lastNode.next,说明node为空的话,lastNode就是最后一个节点,我们已经全部都找过
  73. //分为obj是否为空
  74. if(obj == null){
  75. //如果node.data也为空,那么node就是被删除结点
  76. if(node.data == null){
  77. removeNode = node;
  78. break;
  79. }else{ //否则node和lastNode都往右移动
  80. lastNode = node;
  81. node = node.next;
  82. }
  83.  
  84. }else{
  85. if(obj.equals(node.data)){
  86. removeNode = node;
  87. break;
  88. }else{
  89. lastNode = node;
  90. node = node.next;
  91. }
  92. }
  93. }
  94. if(node.next == null){ //最后一个节点
  95. if(obj == null){
  96. if(node.data == null){
  97. removeNode = node;
  98. }
  99. }else{
  100. if(obj.equals(node.data)){
  101. removeNode = node;
  102. }
  103. }
  104.  
  105. }
  106. if(removeNode == null){
  107. return;
  108. }else{
  109. lastNode.next = removeNode.next;
  110. removeNode.data = null;//使得它尽快被回收
  111. removeNode.next = null;//使得它尽快被回收
  112. }
  113. }
  114. }
  115. total--;
  116.  
  117. }
  118.  
  119. }
  1. public class TestMyLinked {
  2. public static void main(String[] args) {
  3. MyLinked my = new MyLinked();
  4. System.out.println(my.size()); //第一个节点为空,first == null; total==0
  5. my.add("李四");//first:
  6. data:hash==0value[0]李 [1]四;
  7. next:null
  8. my.add("王五");//first:
  9. data:hash==0value[0]李 [1]四;
  10. next: data:hash==0value[0]王 [1]五;
  11. next:null
  12. my.add("赵六");//first:
  13. data:hash==0value[0]李 [1]四;
  14. next: data:hash==0value[0]王 [1]五;
  15. next:data:hash==0value[0]赵 [1]六;
  16. next:null
  17. my.add("Hello");
  18. System.out.println(my.size());
  19. my.remove("赵六");
  20. System.out.println(my.size());
  21. }
  22. }

3、Set(Collection另一个接口)

  1. 一个不包含重复元素的 collection
  2. * 更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 e2,并且最多包含一个 null 元素。
  3. * 正如其名称所暗示的,此接口模仿了数学上的 set 抽象。
  4. * Set的方法都是继承自Collection
  5. Set的实现类们:
  6.  1HashSet:元素是完全无序的,它的顺序是由hash值来决定的。
  7.    HashSet:元素的存储和是否重复要依赖于元素的:hashCode()和equals()方法
  8.  2TreeSet:元素是按照“大小”顺序排列的(字母小的在前),和添加顺序无关。
  9.    TreeSet:元素的存储和是否重复要依赖于元素的比较大小的方法:int compareTo(Object o)或 指定的比较器的int compare(Object o1,Object o2)
  10. @Test
  11. public void test(){
  12. HashSet set = new HashSet();/*set.add("kk");
  13. set.add("alex");
  14. set.add("dd");
  15. set.add("java");
  16. set.add("java");*///String重写了hashCode()和equals()方法,所以只能添加进去一个
  17.               //String实现了java.lang.Comparable接口
  18.     TreeSet set = new TreeSet();
  19. set.add(2);
  20. set.add(1);  //Integer实现了java.lang.Comparable接口
  21. set.add(8);//Integer也重写了hashCode()和equals()方法,所以只能添加进去一个
  22. set.add(8);
  23. for (Object object : set) {
  24. System.out.println(object);
  25. }
  26. }

     TreeSet set = new TreeSet();
      set.add(new Student(2, "kk", 99));
      set.add(new Student(3, "alx", 69));
      set.add(new Student(4, "hk", 69)); //失败的原因,Student没有java.lang.Comparable,无法添加;
                          //Student cannot be cast to java.lang.Comparable
      for (Object object : set) {
        System.out.println(object);
        }

  1. 3LinkedHashSet:是HashSet的子类,但是比HashSet要多一个“记录元素添加顺序的功能”,
  2. * 因此它添加和删除的效率比HashSet相对低。
  3.    * 当你既想要元素不重复,又想要按照添加的顺序时,选择LinkedHashSet
  4.    * 如果你只是想要不重复,那么选择HashSet,如果你执行想要添加顺序,选ArrayList等。
  5. String Integer都重写了hashCode()和equals()方法。

TreeSet:
  (1) 按照元素的自然顺序排列:要求元素必须实现java.lang.Comparable接口
     例如:String实现了java.lang.Comparable接口,它是按照字符的Unicode编码值来排序。

    (2) 按照指定的定制比较器的规则排序,要求指定一个java.util.Comparator接口的实现类对象
               例如:专门为比较String对象,定制了一个比较器java.text.Collator实现了java.util.Comparator接口

实现Comparable接口

  1. public class TestMap2 {
  2. @Test
  3. public void test(){
  4. TreeSet set = new TreeSet();
  5. set.add(new Student(2,"kk",800));
  6. set.add(new Student(1,"gk",100));
  7. set.add(new Student(4,"alx",180));
  8. for (Object object : set) {
  9. System.out.println(object);
  10. }
  11. }
  12. class Student implements Comparable{
  13. private int id;
  14. private String name;
  15. private int score;
  16. public Student() {
  17. super();
  18. }
  19. public Student(int id, String name, int score) {
  20. super();
  21. this.id = id;
  22. this.name = name;
  23. this.score = score;
  24. }
  25. public int getId() {
  26. return id;
  27. }
  28. public void setId(int id) {
  29. this.id = id;
  30. }
  31. public String getName() {
  32. return name;
  33. }
  34. public void setName(String name) {
  35. this.name = name;
  36. }
  37. public int getScore() {
  38. return score;
  39. }
  40. public void setScore(int score) {
  41. this.score = score;
  42. }
  43. @Override
  44. public String toString() {
  45. return "Student [id=" + id + ", name=" + name + ", score=" + score + "]";
  46. }
  47. @Override
  48. public int compareTo(Object o) { //自然排序,按照员工的
  49. Student stu = (Student) o;
  50. //return this.score - stu.score; //按照成绩排序
  51. //return this.id - stu.id; //自然顺序,按照学生id的编号排序
  52. return this.name.compareTo(stu.getName()); //String有它自己的排序方法
  53. }
  54. }

指定一个comparator接口的实现类对象

  1. public class TestMap2 {
  2. @Test
  3. public void Test1(){
  4. TreeSet set = new TreeSet(new Comparator() {
  5. @Override
  6. public int compare(Object o1, Object o2) {
  7. Student stu1 = (Student) o1;
  8. Student stu2 = (Student) o2;
  9. if(stu1.getScore() > stu2.getScore()){
  10. return 1;
  11. }else if(stu1.getScore() < stu2.getScore()){
  12. return -1;
  13. }else{
  14. return 0;
  15. } //因为double类型不精确,建议使用如下方法比较大小
  16. /*long d1 = Double.doubleToLongBits(stu1.getScore());
  17. long d2 = Double.doubleToLongBits(stu2.getScore());
  18. if(d1 > d2){
  19. return 1;
  20. }else if(d1 < d2){
  21. return -1;
  22. }else{
  23. return 0;
  24. }*/
  25. }
  26. });
  27. set.add(new Student(2,"kk",800));
  28. set.add(new Student(1,"gk",100));
  29. set.add(new Student(4,"alx",180));
  30. for (Object object : set) {
  31. System.out.println(object);
  32. }
  33. }
  34. }

java.text.Collator类型比较字符串,可指定语言比较

  1. //指定一个实现了java.util.Comparator接口的实现类的对象
  2. //而且这个比较器还要比较两个“字符串”的大小
  3. //java.text.Collator类型就符合要求
  4.   Collator instance = Collator.getInstance(Locale.CHINA);//Locale.CHINA指定语言环境
  5. TreeSet set = new TreeSet(instance);
  6. set.add("张三");
  7. set.add("李四");
  8. set.add("王五");
  9. for (Object object : set) {
  10. System.out.println(object);
  11. }

4、Map

  1. 将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
  2. Map接口的常用方法:
  3.  1、添加
  4. put(Object key, Object value)
  5. * 任意Java的引用数据类型都可以作为keyvalue的类型,但是最常见的是IntegerString比较多的作为key的类型。
  6. * putAll(Map map):把另一个map集合的映射关系添加到当前Map

    Map map = new HashMap();
    map.put("smile", 78);
    map.put("kris", 78); //value可以一样,key不能一样;

  1.  2、获取映射关系的对数
  2. * int size() //map.size();
  3.  3、查找
  4. * boolean containsKey(Object key)
  5. * boolean containsValue(Object value)
  6. * V get(Object key) :根据key获取value; map.get("key");
  7. * boolean isEmpty()
  8. 4、删除
  9. * V remove(Object key) :根据key移除一对映射关系,并且返回对应的value
  10. * void clear()
  11. 5、遍历
  12.    1Set keySet():因为Set不允许有重复元素,因为Map的所有key也是要求不允许重复的。

    Set keys = map.keySet();
    for (Object object : keys) {
      System.out.println(object);
      }

  1.    (2Collection values() 因为value是可以重复的,所以这里不能用Set

    Collection values = map.values();
      for (Object object : values) {
        System.out.println(object);
        }

  1.   (3Set entrySet()
  2.    因为key不重复,所以entry是不重复的
  3.    Entry类型是Map中的一个内部接口类型,映射项(键-值对)。它的实现类在Map的实现类中,以内部类的形式存在。
         Set entrySet = map.entrySet();
          for (Object object : entrySet) { //Entry接口,可以拿到K, V getKey() getValue()

            System.out.println(object);
               }

  //Can only iterate over an array or an instance of java.lang.Iterable
  //只有数组和Collection系列的集合
  //map不能直接foreach

Map接口的实现类

  1. 所有的Map都是有一个共同点:key不允许重复,即同一个key只能出现一次
  1. 1HashMap:哈希表
  2. 如何确定key的不重复和顺序的:依据key hashCode 方法和 equals 方法
  3. 2TreeMap
  4. 如何确定key的不重复和顺序的:依据key的自然顺序或定制比较器的规则
  5. 因此要求要么key类型实现了java.lang.Comparable接口,并重写int compareTo(Object obj)
  6. 要么在创建TreeMap时指定定制比较器对象(它是实现java.util.Comparator接口,并重写int compare(Object o1, Object o2)的对象)
  7. 3LinkedHashMap
  8. LinkedHashMapHashMap的子类,比HashMap多维护了“添加”的顺序。
  9. 4Hashtable:哈希表
  10. 如何确定key的不重复和顺序的: hashCode 方法和 equals 方法
  11. 5Properties
  12. PropertiesHashtable的子类,是一种特殊的集合,特殊在它的keyvalue的类型都是String
  13. HashMapHashtable都是哈希表:(判断两个key相等的标准:两个keyhashcode值相等且equals方法返回true
  14. * Hashtable:旧版的,线程安全的,不允许keyvaluenull值;
  15. * HashMap:新版的,线程不安全的,允许keyvaluenull值;
  16. 关系:
  17. * HashSetHashMapHashSet其实就是一个HashMap,通过HashMap实现
  18. 添加到HashSet中的元素是作为HashMapkey,他们的value是共享了一个PRESENT(是Object类型的常量对象)
  19. * TreeSetTreeMap:TreeSet其实就是一个TreeMap
  20. 添加到TreeSet中的元素是作为TreeMapkey,他们的value是共享了一个PRESENT(是Object类型的常量对象)
  21. * LinkedHashSetLinkedHashMapLinkedHashSet其实就是一个LinkedHashMap

练习

  1. @Test
  2. public void test1(){
  3. TreeMap map = new TreeMap();
  4. //第一组
  5. ArrayList list = new ArrayList();
  6. list.add(new Student1("kris", 99));
  7. list.add(new Student1("jing", 89));
  8. list.add(new Student1("smile", 79));
  9. map.put("第一组", list);
  10. //第二组
  11. ArrayList list2 = new ArrayList();
  12. list2.add(new Student1("张三",56));
  13. list2.add(new Student1("李四",78));
  14. map.put("第二组", list2);
  15. //第三组
  16. ArrayList list3 = new ArrayList();
  17. list3.add(new Student1("王五",99));
  18. list3.add(new Student1("嘿嘿",45));
  19. map.put("第三组", list3);
  20. Set entrySet = map.entrySet();
  21. /*for (Object object : entrySet) {
  22. System.out.println(object);
  23. }*/
  24. double max = -1;
  25. for (Object object : entrySet) {
  26. Entry entry = (Entry) object; // Entry类型是Map中的一个内部接口类型,映射项(键-值对)。它的实现类在Map的实现类中,以内部类的形式存在
  27. Object group = entry.getKey();
  28. System.out.println(group);
  29. System.out.print("该组学员有:");
  30. ArrayList listvalue = (ArrayList)entry.getValue();
  31. for (Object stu : listvalue) {
  32. System.out.println(stu);
  33. Student1 s = (Student1) stu;
  34. if(s.getScore() > max){
  35. max = s.getScore();
  36. }
  37. }
  38. }System.out.println("最高分为:" + max);
  39. }

5、HashMap底层实现

存储到HashMap中的映射关系(key,value),其中的key的hashCode值和equals方法非常重要。

单向链表,如果查找n个元素中的最后一个元素会遍历n遍;如5个元素,查找5遍;

而二叉树分left和right,5个元素查找最后一个则只查找3遍;

  1. * HashMap的底层实现:
  2. * 1、简单版
  3. * JDK1.7HashMap的底层实现是:数组+单向链表
  4. * JDK1.8HashMap的底层实现是:数组+单向链表/红黑树
  5.  
  6. 为什么要红黑树?
  7. * 红黑树:一个自平衡的二叉树
  8. * 当结点多了用红黑树,少了用链表
  9. * 因为少的话用红黑树太复杂,多了话用红黑树可以提高查询效率。
  10. * 红黑树:(自动调整根结点,保证左右两边的结点数差不多),它会左旋,右旋来实现。
  11.  
  12. 2、复杂v1.0
  13. * JDK1.7HashMap的底层实现是:数组(初始长度为16)+单向链表
  14. * 每一个对映射关系的存储位置:
  15. * 存储的位置:存储的位置和顺序是否重复,是依据keyhashCode()和equal()决定的;
  16. 1)先计算keyhash值,通过hashCode()就可以得到,
  17. 2)再用hash值经过各种(异或^)操作,得到一个int数字,目的是使得更均匀的分布在数组的每一个元素中。
  18. 3)再用刚才的int值,与table数组的长度-1int & table.length)做一个“按位与&"运算,目的是得到一个[i]
  19. 因为数组的长度是2的n次方,长度-1的数的二进制是前面都是0,后面都是1,任何数与它做“&”,结果一定是[0,该数]之间
  20.  
  21. 为什么会出现链表?
  22. * (1)两个不同的key对象,仍然可能出现hashCode值一样
  23. * (2)就算hashCode不一样,经过刚才的计算,得到[i]是一样的
  24. * (3)而我们的数组的元素table[i]本来只能放一个元素,那么现在有多个元素需要存储到table[i]中,只能把这几个元素用链表连接起来
  25. * 简单比喻:
  26. * y = f(x)
  27. * 两个不一样的x,可能得到一样的y;
  28.  
  29. 那么存储到HashMap中的是什么样的元素呢?
  30. * (1)首先是Map.Entry类型:映射项(键-值对)。
  31. * (2)其次HashMap有一个内部类HashMap.Entry实现了Map.Entry接口类型
  32. * 内部类Entry由四部分组成:
  33. * (1)key
  34. * (2)value
  35. * (3)下一个Entry:next
  36. * (4)hash值计算的整数值,为了后面查询快一点
  37.  
  38. 如何避免重复?(hash值取的是整数在内存中做比较比较快,而equals重写可能会比较很多次)
  39. * 如果两个key的hash值是一样的,还要调用一下equlas()方法,如果返回true,就会把新的value替换旧的value。
  40. * 如果两个key的hash值是一样的,还要调用一下equlas()方法,如果返回false,就会把新的Entry连接到旧的Entry所在链表的头部(first)
  41. * 如果两个key的hash值是不一样的,但是[i]是一样的,就会把新的Entry连接到旧的Entry所在链表的头部(first)
  42. * 如果两个key的hash值是不一样的,并且[i]不一样的,肯定存在不同table[i]中
  43. * 我们把table[i]称为“桶bucket”。
  44. 回忆:两个对象的hash值:
  45. * (1)如果两个对象是“相等”,他们的hash值一定是一样
  46. * (2)如果两个对象的hash值是一样,他们可能是相同的对象,也可能是不同的对象;。
  47. *
  48. * 3、复杂追踪源代码v2.0版
  49. (1)什么时候扩容
  50. * 当①元素的个数达到“阈值”,②并且新添加的映射关系计算出来的table[i]位置是非空的情况下,再扩容;(如果table[i]是空的就不扩容直接放进去,若是非空说明是连接到别的下面了;)
  51. * 默认加载因子 (0.75):DEFAULT_LOAD_FACTOR,也可自己手动赋值
  52. * 阈值 = table.length * 加载因子(loadFactor)
  53. * 第一次阈值:16 * 0.75 = 12 默认初始容量:DEFAULT_INITIAL_CAPACITY:16 (1<<4即2^4)
  54. 第二次阈值:32 * 0.75 = 24
  55. * ...
  56. *
  57. * HashMap中table的长度有一个要求:必须是2的n次方(如果你给的值不是2^n,它也会给你纠正一个最接近的值),是为了让2^n-1的二进制是11111...
  58.  
  59. (2)跟踪一下put方法的源代码
  60. * 第一步:先判断table是否为空数组,如果table是空的,会先把table初始化为一个长度为16的数组(在默认的构造器中创建);
            如果你指定的长度不是2的n次方,会往上纠正为最接近的2的n次方(int capacity = roundUpToPowerOf2(toSize)),
            并且把阈值threshold = table.length * 加载因子(loadFactor) = 12。
  61. * 第二步:如果key是null,(空的就直接添加进去) 首先确定的位置是table[0],
  62. * 如果原来table[0]已经有key为null的Entry,用新的value替换旧的value
  63. * 如果原来table[0]没有key为null的Entry,那么创建一个新的Entry对象,作为table[0]桶下面的链表的头,原来的那些作为它next。
  64. *
  65. * 第三步:如果key不是null(要先看看key是否相同),先计算hash(key),用key的hashCode值,通过hash()函数算出一个int值称为"hash"
  66. * 第四步:通过刚才的“hash”的int值与table.length-1做&运算,得到一个下标index,表示新的映射关系即将存入table[index]
  67. * 第五步:循环判断table[index]位置是否为空,并且是否有Entry的key与我新添加的key是否“相同”,如果相同,就用新的value替换旧的value
  68. * 第六步:添加新的映射关系
  69. * (1)判断是否要扩容:
  70. * 当元素的个数达到“阈值”,并且新添加的映射关系计算出来的table[i]位置是非空的情况下,table再扩容为原来的2倍长
  71. * 如果扩容了,那么要重新计算hash和index
  72. * (2)把新的映射关系创建为一个Entry的对象放到table[index]的头部。
  73. */
  1. @SuppressWarnings("all")
  2. public class TestHashMapSource17 {
  3. @Test
  4. public void test1(){
  5. HashMap map = new HashMap();
  6.  
  7. map.put(1, "哈哈");
  8. map.put(2, "嘻嘻");
  9. map.put(3, "呵呵");
  10. map.put(4, "嘿嘿");
  11. }
  12.  
  13. @Test
  14. public void test2(){
  15. HashMap map = new HashMap();
  16.  
  17. map.put( "哈哈",1);
  18. map.put( "嘻嘻",2);
  19. map.put("呵呵",3);
  20. map.put("嘿嘿",4);
  21. }
  22.  
  23. @Test
  24. public void test3(){
  25. HashMap map = new HashMap();
  26.  
  27. map.put(new MyData(1), "哈哈");
  28. map.put(new MyData(2), "嘻嘻");
  29. map.put(new MyData(3), "呵呵");
  30. map.put(new MyData(4), "嘿嘿");
  31. }
  32.  
  33. @Test
  34. public void test4(){
  35. HashMap map = new HashMap();
  36. for (int i = 1; i <= 35; i++) {
  37. map.put(i, "第" + i + "个value值");
  38. }
  39. }
  40.  
  41. @Test
  42. public void test5(){
  43. HashMap map = new HashMap();
  44. for (int i = 1; i <= 35; i++) {
  45. map.put(new MyData(i), "哈哈" + i);
  46. }
  47. }
  48.  
  49. }
  50.  
  51. class MyData{
  52. private int num;
  53.  
  54. public MyData(int num) {
  55. super();
  56. this.num = num;
  57. }
  58.  
  59. @Override
  60. public int hashCode() {
  61. /*final int prime = 31;
  62. int result = 1;
  63. result = prime * result + num;
  64. return result;*/
  65. return 1;//所有对象的hash值都是1,这个时候肯定会出现链表
  66. }
  67.  
  68. @Override
  69. public boolean equals(Object obj) {
  70. if (this == obj)
  71. return true;
  72. if (obj == null)
  73. return false;
  74. if (getClass() != obj.getClass())
  75. return false;
  76. MyData other = (MyData) obj;
  77. if (num != other.num)
  78. return false;
  79. return true;
  80. }
  81.  
  82. }

  1. * JDK1.8:HashMap的底层实现是:数组+链表/红黑树
  2. *
  3. * 1、复杂回答v1.0
    jdk1.7jdk1.8的异同点:
  4. * 1)映射关系的类型 TreeNode-->Entry-->Node-->Object
  5. * 添加到HashMap1.8种的映射关系的对象是HashMap.Node类型(原来是HashMap.Entry),
     或者是HashMap.TreeNode类型。
  6. * 2)映射关系添加到table[i]中时,如果里面是链表,新的映射关系是作为原来链表的尾部; JDK1.7是只要是新添加的就放在首位;
  7. * “七上八下”:JDK1.7在上,JDK1.8在下。
  8. 为什么要在下面? 因为如果是红黑树,那么是在叶子上,保持一致,都在下面。
  9. * 3)扩容的时机不同
  10. * 第一种扩容:元素个数size达到阈值threshod = table.length * 加载因子 并且table[i]是非空的;(这种情况(都存在数组中均匀分布,不涉及链表)JDK1.7是一样的;)
  11. * 第二种扩容:当某个桶table[index]下面的结点的总数原来已经达到了8个,这个时候,要添加第9个时(由16变成32),会检查
  12. * table.length是否达到64(基本上我们的元素很少达到64个),如果没达到就扩容; 如果添加第10个时,也会检查table.length是否达到64,如果没达到就扩容。
  13. * 为什么? 因为一旦扩容,所有的映射关系要重新计算hashindex,一计算原来table[index]下面(单向链表)可能就没有8个,或新的映射关系也可能不在table[index]
  14. * 如果链表特别长是不利于查找的,如果均匀分布在数组中查找会很快(通过hash马上就找到了);这样就可能均匀分布。
  15. ----------------------------------------------------------------------
  16. * 4)什么时候从链表HashMap$Nodehash key next value变成红黑树HashMap$TreeNodeleft parent right
  17. * table[index]下面结点的总数已经达到8个,并且table.length也已经达到64,(添加第11个时)那么再把映射关系添加到table[index]下时,就会把原来的链表修改红黑树
  18.  
  19. * 5)什么时候会从红黑树变为链表?
  20. * 当删除映射关系时table[index]下面结点的总数少于6个,会把table[index]下面的红黑树变回链表。
  21.  
  22. * 2put的源代码v2.0
  23. * 第一步:计算keyhash,用了一个hash()函数,目的得到一个比hashCode更合理分布的一个int值;(return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);)
  24. * 第二步:如果table是空的,那么把table初始化为长度为16的数组,阈值初始化为= 默认的长度16 * 默认的加载因子0.75 = 12
  25.    DEFAULT_INITIAL_CAPACITY:默认的初始化容量16
  26.    DEFAULT_LOAD_FACTOR:默认加载因子 0.75
  27. * 第三步:查看table[index]是否为空,如果为空,就直接(添加到数组这种情况new一个Node放进去
  28.      index = hashint & table.length-1
  29. * 第四步:不为空,先查看table[index]的根节点的key是否与新添加的映射关系的key是否“相同”(单向链表这种情况),如果相同,就用新的value替换原来的value
  30. * 第五步:如果table[index]的根节点的key与新添加的映射关系的key不同,
  31.     还要看table[index]根结点的类型是Node还是TreeNode类型,
  32.     如果是Node类型,那么就查看链表下的所有节点是否有key与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value
  33.     如果是TreeNode类型,那么就查看红黑树下的所有叶子节点的key是否与新添加的映射关系的key是否“相同”,如果相同,就用新的value替换原来的value
  34.   
  35. * 第六步:如果没有找到table[index]有结点与新添加的映射关系的key“相同”,那么
  36.     如果是TreeNode类型,那么新的映射关系就创建为一个TreeNode,连接到红黑树中
  37.      如果是Node类型,那么要查看链表的结点的个数,是否达到8个,如果8个,并且table.length小于64(最小树化容量),那么先扩容,一旦扩容就会进行重写计算。
  38.     TREEIFY_THRESHOLD:树化阈值8,达到TREEIFY_THRESHOLD - 1 = 7时就准备树化;
  39.     MIN_TREEIFY_CAPACITY:最小树化容量64
  40.  
  41.    UNTREEIFY_THRESHOLD:反树化,从树变为链表的阈值6

  几个常量和变量值的作用:

    ①默认负载因子static final float DEFAULT_LOAD_FACTOR = 0.75f;

    ②负载因子final float loadFactor;

  1.    ③阈值int threshold;当size达到threshold阈值时,会扩容;

    ④树化阈值static final int TREEIFY_THRESHOLD = 8;

      该阈值的作用是判断是否需要树化,树化的目的是为了提高查询效率;当某个链表的结点个数达到这个值时,可能会导致树化。

     ⑤树化最小容量值static final int MIN_TREEIFY_CAPACITY = 64;当某个链表的结点个数达到8时,还要检查table的长度是否达到64,如果没有达          到,先扩容解决冲突问题

  1.    ⑥反树化阈值static final int UNTREEIFY_THRESHOLD = 6;
  1. @SuppressWarnings("all")
  2. public class TestHashMapSource18 {
  3. @Test
  4. public void test1(){
  5. HashMap map = new HashMap();
  6. map.put(1, "哈哈");
  7. map.put(2, "嘻嘻");
  8. map.put(3, "呵呵");
  9. map.put(4, "嘿嘿");
  10. }
  11.  
  12. @Test
  13. public void test2(){
  14. HashMap map = new HashMap();
  15.  
  16. map.put( "哈哈",1);
  17. map.put( "嘻嘻",2);
  18. map.put("呵呵",3);
  19. map.put("嘿嘿",4);
  20. }
  21.  
  22. @Test
  23. public void test3(){
  24. HashMap map = new HashMap();
  25.  
  26. map.put(new MyData(1), "哈哈");
  27. map.put(new MyData(2), "嘻嘻");
  28. map.put(new MyData(3), "呵呵");
  29. map.put(new MyData(4), "嘿嘿");
  30. }
  31.  
  32. @Test
  33. public void test4(){
  34. HashMap map = new HashMap();
  35. for (int i = 1; i <= 35; i++) {
  36. map.put(i, "第" + i + "个value值");
  37. }
  38. }
  39.  
  40. @Test
  41. public void test5(){
  42. HashMap map = new HashMap();
  43. for (int i = 1; i <= 35; i++) {
  44. map.put(new MyData(i), "哈哈" + i);
  45. }
  46. }
  47.  
  48. }
  49.  
  50. class MyData{
  51. private int num;
  52.  
  53. public MyData(int num) {
  54. super();
  55. this.num = num;
  56. }
  57.  
  58. @Override
  59. public int hashCode() {
  60. /*final int prime = 31;
  61. int result = 1;
  62. result = prime * result + num;
  63. return result;*/
  64. return 1;//所有对象的hash值都是1,这个时候肯定会出现链表
  65. }
  66.  
  67. @Override
  68. public boolean equals(Object obj) {
  69. if (this == obj)
  70. return true;
  71. if (obj == null)
  72. return false;
  73. if (getClass() != obj.getClass())
  74. return false;
  75. MyData other = (MyData) obj;
  76. if (num != other.num)
  77. return false;
  78. return true;
  79. }
  80.  
  81. }
  1. @Test
  2. public void test1(){
  3. HashMap<Integer, String> map = new HashMap<>();
  4. map.put(1, "kris");
  5. map.put(2, "alex");
  6. map.put(3, "smile");
  7. }

put的过程:

 [1],int算完的hash=1 & 15 --> 1,所以下标为1

以字符串作为key,数字为value;----->>

HashMap和TreeMap、LinkedHashMap的内部实现类型,每个版本分别是什么?

HashMap的key的hashCode值的作用是什么?hash()方法的作用是什么?
  hashCode值的作用是为了计算table[index]索引位置,协助equals方法保证key的不重复。
  hash()方法的作用是得到一个int值,这个int值可以使得映射关系更均匀的分布在table数组中。

HashMap的key的equals()方法的作用是什么?
  key的equals()方法的作用是确保key的不重复

HashMap和CurrentHashMap的区别?

HashMap的初始容量和加载因子会影响它的性能;初始容量默认值是16,加载因子(是hash表在增加前可以达到多满的尺度)默认值是0.75,容量(hash表中桶的数量);

当hash表中容量 > 16*0.75=12,会进行扩容resize;

HashMap的寻址方式:对于新插入或者读取的数据,key-->hash值对数组长度取模,结果作为它在数组中查找的index,取模的代价在计算机中比较高,所以数组长度要是2^n;

key的hash值 和2^-1做与运行,结果与取模操作是相同的;

HashMap是线程不安全的;体现在resize时可能出现死循环,使用迭代器;扩容时创建一个新的是原来容量2倍的新数组,将原来的重新插入到新数组;

多线程rehash就可能出现问题;

currentHashMap底层仍然是数组+链表,与hashmap不同的是它的外层不是一个大的数组,而是一个三维的数组,每个数组包含与hashmap类似的链表;

在读取某个key的hash值,对Segment的N位取模得到该key属于哪个segment;接着就像操作hashmap时操作segment,保证不同的值分布均匀,segment时继承JUC里边的

可以很方便的对segment上锁;

不同点:线程安全;hashmap允许key value为空,而currenthashmap则不允许;hash不允许在遍历时修改,currenthashmao运行并且时可见的;

java8废弃了分段锁,并且使用了一个大的数组,为了提高碰撞、寻址做了性能优化;数组长度默认是8,链表换成了红黑树,寻址时间复杂度由On变成OLogn,key的hash与数组长度取模得到key在数组中索引;

jdk1.8

6.集合工具类

  1. java.util.Collections
  2. * 1public static <T> boolean addAll(Collection<? super T> c,T... elements)
  3.   将后面可变参数传递的实参对象全部都添加到c集合中
  4. * 2public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key)
  5.    list集合中,查找key的索引
  6. * 3public static <T> void copy(List<? super T> dest, List<? extends T> src) -- >>既有上限又有下线,T相当于是它俩的交集。
  7.   src的集合中的元素复制到dest集合
  8. * 4public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
  9.   coll集合中找最小值
  10. * 这里为什么要写Object?因为当泛型擦除时,按照上限的第一个类型处理,泛型是JDK1.5才有的,
  11.   JDK1.5之前,没有泛型这个方法,返回值类型是按照Object处理的,为了当泛型擦除时与低版本保持一致
  12. * 5public static <T extends Comparable<? super T>> void sort(List<T> list)
  13.   这个T的要求必须实现Comparable接口,也可以是T的父类实现了Comparable接口
  14. * 6public static <T> void sort(List<T> list, Comparator<? super T> c)
  15.    要指定定制比较器,这个定制比较器中的T,必须是TT的父类
  16. *
  17. * 7synchronizedXxx开头的方法:表示把一个集合对象转成线程安全的
  18. * 8unmodifiableXxx开头的方法:表示把一个集合对象变成只读的
  19. *
  20. * java.util.Arrays
  21. * public static <T> List<T> asList(T... a):把可变参数的实参,都放到一个List的集合并返回
  1. @Test
  2. public void test1(){
  3. ArrayList<String> list1 = new ArrayList<>();
  4. ArrayList<Object> list2 = new ArrayList<>(); //传它的父类Object也可以
  5.  
  6. Collections.addAll(list1, "Hello", "World");
  7.  
  8. int binarySearch = Collections.binarySearch(list1, "Hello");
  9. //这个集合的泛型(1)看T的类型(2)它必须实现java.lang.Comparable接口; 因为String实现了Comparable方法
  10.  
  11. System.out.println(binarySearch);//
  12.  
  13. for (String object : list1) {
  14. System.out.println(object);
  15. }
  16. }
  17.  
  18. @Test
  19. public void test2(){
  20. List<Number> src = new ArrayList<>();
  21. List<Number> dest = new ArrayList<>();
  22. List<Object> dest2 = new ArrayList<>();//看把Number放到Object或者Integer中行不行
  23. // List<Integer> dest1 = new ArrayList<>();//错误的
  24. Collections.copy(dest, src); //把src复制到dest
  25.  
  26. ArrayList<String> list = new ArrayList<>();
  27. ArrayList<Object> list2 = new ArrayList<>(); //因为Object没有实现Comparable接口
  28. ArrayList<Integer> list3 = new ArrayList<>();
  29. // ArrayList<xx> list = new ArrayList<>();
  30. Collections.min(list);
  31.  
  32. }
  33.  
  34. public void test3(){
  35. List<String> list = Arrays.asList("Hello", "kris","java");
  36. //list.add("k"); //UnsupportedOperationException
  37. //这个返回的是一个内部的Arrays.ArrayList,不是我们平时的ArrayList它是只读的。
  38. for (String string : list) {
  39. System.out.println(string);
  40. }
  41.  
  42. }

JavaSE| 集合的更多相关文章

  1. javase集合 温故而知新

    复习javase集合 1.为什么要有集合? 数组长度需要在初始化时确定大小,数据结构单一.因此集合出现了 2.数组和集合的区别 区别一:数组既可以存储基本数据类型,又可以存储引用类型,集合只能存储引用 ...

  2. JavaSE集合(十)之Map

    前面给大家介绍了集合家族中的Collection家族,这一篇给大家分享的是集合中的另一个家族就是Map家族.以前的时候学习Map的时候没有很认真的去学习,我觉得很多东西还是不是很清楚. 这次我将总结的 ...

  3. JavaSE集合(八)之Map

    前面给大家介绍了集合家族中的Collection家族,这一篇给大家分享的是集合中的另一个家族就是Map家族.以前的时候学习Map的时候没有很认真的去学习,我觉得很多东西还是不是很清楚. 这次我将总结的 ...

  4. JavaSE集合基础总览

    Java集合 Java集合,也称之为容器.基本上你写所有的Java程序,都必须要用到一个包.该API基本都位于java.util工具类包中,是JavaSE中的重中之重.简单可以总结为“1136”,分别 ...

  5. JavaSE 集合概述

    1.对象的存储: 数组(基本数据类型 & 引用数据类型) 集合(引用数据类型) 2.集合框架 Collection 接口: 方法: iterator().toArray();  迭代器遍历集合 ...

  6. [javaSE] 集合框架(体系概述)

    为什么出现集合类 为了方便对多个对象的操作,对对象进行存储,集合就是存储对象最常用的一种方式 数组和集合的不同 数组是固定长度的,集合是可变长度的 数组可以存储基本数据类型,集合只能存储对象 数组只能 ...

  7. JavaSE 集合补充点(JDK1.9对集合添加的优化)

    通常,我们在代码中创建一个集合(例如,List 或 Set ),并直接用一些元素填充它. 实例化集合,几个 add方法调用,使得代码重复. public class Demo01 { public s ...

  8. [javaSE] 集合框架(共性方法)

    Collection接口的常用方法 add(),添加一个元素 addAll(),添加一组元素 clear(),清空 remove(),移除一个 removeAll(),移除一组 size(),元素个数 ...

  9. [javaSE] 集合框架(TreeSet)

    TreeSet:可以对Set集合中的元素排序,默认按照ascii表排序,二叉树结构 左边叉是小的,右边叉是大的 存储自定义对象 定义一个类Student实现Comparable类,使自定义类具备比较性 ...

随机推荐

  1. [C]二级指针

    二级指针即“指向指针的指针”: 下面的实例代码创建了一个二级指针c int a = 5; int* b = &a; int** c = &b; 你不能这样 int a = 5; int ...

  2. flex下部固定高,上部不固定,而且超过内容要滚动

    <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...

  3. jquery通过visible来判断标签是否显示或隐藏

    if($(".spnTotal").is(":visible")==false) { alert('隐藏'); } else { alert('显示'); } 

  4. Hive shell 基本命令

    首先连接 hive shell 直接输入 hive启动, 使用--开头的字符串来表示注释 hive>quit; --退出hive hive> exit; --exit会影响之前的使用,所以 ...

  5. 最长上升子序列(dp)

    链接:https://www.nowcoder.com/questionTerminal/d83721575bd4418eae76c916483493de来源:牛客网 广场上站着一支队伍,她们是来自全 ...

  6. 在前台根据传过来的XX级别的数字转XX的名字

    需求描述:进入页面,展示列表,列表中有个XX级别的项,数据库中的级别使用1234来存放的,现在要转成对应的一级XX,二级XX,三级XX,四级XX. 吐槽一下:正常的做法应该是在后台,就把查出来的级别1 ...

  7. Deal with Warning: mysqli::__construct(): (HY000/2002)

    1.安装XAMPP之后,如果之前安装过MySQL或者 apache,启动XAMPP中的响应的服务的时候回出现报错, 出错的原因,具体看报错的原因,如果是端口占用,在配置中修改端口号,如果是提示 “Fo ...

  8. python(5):scipy之numpy介绍

    python 的scipy 下面的三大库: numpy, matplotlib, pandas scipy 下面还有linalg 等 scipy 中的数据结构主要有三种: ndarray(n维数组), ...

  9. MySQL----数据库练习

    一.多对多的正反向查询 class Class(models.Model): name = models.CharField(max_length=32,verbose_name="班级名& ...

  10. cf1110F 离线+树上操作+线段树区间更新

    自己搞的算法超时了..但是思路没什么问题:用线段树维护每个点到叶子节点的距离即可 /* 线段树维护区间最小值,每次向下访问,就把访问到的点对应的区间段减去边权 到另一颗子树访问时,向上回溯时加上减去的 ...