zoukankan      html  css  js  c++  java
  • Java中的集合

    Java中的集合

    • 集合分为:List,Set,Map三种,其中List与Set是继承自Collection,而Map不是。

    一、List与Set的区别:

    List中的元素有存放顺序,并且可以存放重复元素,检索效率高,插入删除效率低;

    Set没有存放顺序,而且不可以存放重复元素,后来的元素会把前面重复的元素替换掉,检索效率低,插入删除效率高。(Set存储位置是由它的HashCode码决定的,所以它存储的对象必须有equals()方法,而且Set遍历只能用迭代,因为它没有下标。)(参考1)

    二、ArrayList与LinkList的区别:

    ArrayList基层是以数组实现的,可以存储任何类型的数据,但数据容量有限制,超出限制时会扩增50%容量,把原来的数组复制到另一个内存空间更大的数组中,查找元素效率高。ArrayList是一个简单的数据结构,因超出容量会自动扩容,可认为它是常说的动态数组。

    LinkList以双向链表实现,链表无容量限制,但双向链表本身使用了更多空间,每插入一个元素都要构造一个额外的Node对象,也需要额外的链表指针操作。允许元素为null,线程不安全。(参考2)

    ArrayList和LinkList都实现了List接口,LinkList还额外实现了Deque接口。

    综合比较结论:(参考3)

    LinkList在增、删数据效率方面要优于ArrayList,而ArrayList在改和查方面效率要远超LinkList。

    ArrayList查询直接根据下标查询,而LinkList需要遍历才能查到元素。增加时直接增加放在队尾,指定位置增加时,数组的元素会往后移动,而链表则需要进行先遍历到指定的位置。

    但是,虽然综合比较之下LinkList的优势要比ArrayList要好,但是在java中,我们用的比较多的确实ArrayList,因为我们的业务通常是对数据的改和查用的比较多。线程是非安全的。

    三、Map之间的区别

    HashMap

    数组
    数组存储区间连续,占用内存比较严重,空间复杂度很大。但数组的二分查找时间复杂度小,为O(1);

    数组的特点是:寻址容易,插入和删除困难;

    链表
    链表存储区间离散,占用内存比较宽松,空间复杂度很小,但时间复杂度很大,达O(N)。

    链表的特点是:寻址困难,插入和删除容易。

    HashMap

    (1.7 数组+链表;1.8 数组+链表+红黑树)是用得最多的一种键值对存储的集合, 用一个数组来存储元素,但是这个数组存储的不是基本数据类型。HashMap实现巧妙的地方就在这里,数组存储的元素是一个Entry类,这个类有三个数据域,key、value(键值对),next(指向下一个Entry) 。

    特点:HashMap允许空键值,并且它是非线程安全的,所以插入、删除和定位元素会比较快。

    说一下HashMap的put方法

    大体流程:

    1. 根据key通过哈希算法拿到一个HashCode结合与操作,与数组的长度-1进行运算,得到一个数组下标

    2. 如果得到的数组下标位置元素为空,则将key和value封装成Entry对象(1.7为Entry对象,1.8为Node对象)并放入该位置。

    3. 如果下标元素不为空,需要分情况讨论

      a. 1.7 首先需要判断需不需要扩容,需要的话先扩容,如果不需要扩容则将Key和Value封装成Entry对象,采用头插法插入当前位置链表中。

      b. 1.8 需要先判断是红黑树Node还是链表Node

      • 如果是红黑树则需要将Key和Value封装成红黑树节点添加到红黑树中,在添加的过程中会判断是否包含节点,如果包含则更新
      • 如果是链表则先将Key和Value封装成Node节点,采用尾插法进行插入,如果插入的过程中包含此节点则更新,如果没有则插入到最后。插入完之后如果节点个数大于等于8则转为红黑树存储。
      • 插入完成之后则判断是否需要扩容,需要扩容则扩容,不需要则结束PUT方法。

    Hashcode有什么作用

    HashMap 的添加、获取时需要通过 key 的 hashCode() 进行 hash(),然后计算下标 ( n-1 & hash),从而获得要找的同的位置。当发生冲突(碰撞)时,利用 key.equals() 方法去链表或树中去查找对应的节点。

    Hash

    Hash是散列的意思,就是把任意长度的输入,通过散列算法变换成固定长度的输出,该输出就是散列值。关于散列值,有以下几个关键结论:

    • 如果散列表中存在和散列原始输入K相等的记录,那么K必定在f(K)的存储位置上
    • 不同关键字经过散列算法变换后可能得到同一个散列地址,这种现象称为碰撞
    • 如果两个Hash值不同(前提是同一Hash算法),那么这两个Hash值对应的原始输入必定不同
    HashCode
    • HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的
    • 如果两个对象equals相等,那么这两个对象的HashCode一定也相同
    • 如果对象的equals方法被重写,那么对象的HashCode方法也尽量重写
    • 如果两个对象的HashCode相同,不代表两个对象就相同,只能说明这两个对象在散列存储结构中,存放于同一个位置

    总结

    1. hashCode() 在散列表中才有用,在其它情况下没用

    2. 哈希值冲突了场景,hashCode相等,但equals不等

    3. hashcode:计算键的hashcode作为存储键信息的数组下标用于查找键对象的存储位置

    4. equals:HashMap使用equals()判断当前的键是否与表中存在的键相同。

      • 如果两对象equals()是true,那么它们的hashCode()值一定相等,

      • 如果两对象的hashCode()值相等,它们的equals不一定相等(hash冲突啦)

      • 注:Hash冲突的四种解决办法

    TreeMap

    是基于红黑树实现的,适用于按自然顺序火兹定于顺序遍历key。

    HashTable

    是基于HashCode实现的(数组+链表),但它是线程安全的,无论key还是value都不能为null,所以会比HashMap效率低,而且不允许null值。

    ConcurrentHashMap

    (分段数组+链表),线程安全。

    四、Set中最常用的集合:HashSet

    HashSet是使用Hash表实现的,集合里面的元素是无序得,可以有null值,但是不能有重复元素。(参考1)

    特点:因为相同的元素具有相同的hashCode,所以不能有重复元素

    六、针对ArrayList的操作

    • List 包含的方法(参考5)
    1. add(Object element): 向列表的尾部添加指定的元素。

    2. size(): 返回列表中的元素个数。

    3. get(int index): 返回列表中指定位置的元素,index从0开始。

    4. add(int index, Object element): 在列表的指定位置插入指定元素。

    5. set(int i, Object element):

      将索引i位置元素替换为元素element并返回被替换的元素。

    6. clear(): 从列表中移除所有元素。

    7. isEmpty():

      判断列表是否包含元素,不包含元素则返回 true,否则返回false。

    8. contains(Object o): 如果列表包含指定的元素,则返回 true。

    9. remove(int index): 移除列表中指定位置的元素,并返回被删元素。

    10. remove(Object o):

      移除集合中第一次出现的指定元素,移除成功返回true,否则返回false。

    11. iterator(): 返回按适当顺序在列表的元素上进行迭代的迭代器。

    • 排序

      Collections.sort(list);       //针对一个ArrayList内部的数据排序
      
      如果想自定义排序方式则需要有类来实现Comparator接口并重写compare方法
      调用sort方法时将ArrayList对象与实现Commparator接口的类的对象作为参数
      
      -- 调用
      Collections.sort(list, new SortByAge());
      
      -- 实现接口
      
      class SortByAge implements Comparator {
         public int compare(Object o1, Object o2) {
           Student s1 = (Student) o1;
           Student s2 = (Student) o2;
            return s1.getAge().compareTo(s2.getAge());
        }
      }
      
      注:compareTo方法比较字符串,但是也可以比较数字(数字为String类型)			
      原理:先比较字符串长度,再逐一转成char类型去比较字符串里的每一个字符。
      
    • 遍历

      -- for循环的遍历方式
      for (int i = 0; i < lists.size(); i++) {
         System.out.print(lists.get(i));
      }
      
      -- foreach的遍历方式
      for (Integer list : lists) {
         System.out.print(list);
      }
      
      -- Iterator的遍历方式
      for (Iterator list = lists.iterator(); list.hasNext();) {
         System.out.print(list.next());
      }
      
    • 删除

      lists.remove(6);        //指定删除
      
      List 删除元素的逻辑是将目标元素之后的元素往前移一个索引位置,
      最后一个元素置为 null,同时 size - 1。
      遍历删除时,操作不当会导致异常,
      原因:ArrayList 中两个 remove() 方法都对 modCount 进行了自增,
      那么我们在用迭代器迭代的时候,若是删除末尾 的元素,
      则会造成 modCount 和 expectedModCount 的不一致导致异常抛出。
          
      Iterator 迭代遍历删除是最安全的方法。(参考4)
      
      -- 示例:
      public static void remove(List list, String target){
            Iterator iter = list.iterator();
              while (iter.hasNext()) {
                 String item = iter.next();
                 if (item.equals(target)) {
                  iter.remove();
              }
            }
             System.out.println(list);
          }
      
    • 去重(参考6)

    1. 利用HashSet(不保证元素顺序一致)

    2. 利用LinkedHashSet (去重后顺序一致),继承的父类HashSet

    参考资料

  • 相关阅读:
    UIautomator Python测试
    selenium提供了三种模式的断言:assert,verify,waitfor
    编译标志
    模块级函数
    执行匹配
    反斜杠
    正则表达式
    重复
    字符匹配
    贪婪 vs 不贪婪
  • 原文地址:https://www.cnblogs.com/renxiuxing/p/14970213.html
Copyright © 2011-2022 走看看