集合

集合框架图

 

 

如何重写hashCode

整理出你判断对象相等的属性,然后取一个尽可能小的正整数(尽可能小时怕最终得到的结果超出了整型int的取数范围),这里我取了17,(好像在JDK源码中哪里看过用的是17),然后计算17*属性的hashcode+其他属性的hashcode,重复步骤。

复制代码
@Override
    public int hashCode() {
        int result = name.hashCode();
        result = 17 * result + sex.hashCode();
        result = 17 * result + age.hashCode();
        return result;
}

Iterator

Iterator(迭代器)是一个接口,作为collection的方法,它的作用就是遍历容器的所有元素,它与 Collection 和 Map 系列的集合不一样,Collection 和 Map 系列集合主要用于盛装其他对象,而 Iterator 则主要用于遍历(即迭代访问)Collection 集合中的元素,同时在遍历时不允许修改值.

Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口。Iterator 接口里定义了如下 4 个方法。

  • boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回 true。
  • Object next():返回集合里的下一个元素。
  • void remove():删除集合里上一次 next 方法返回的元素。
  • void forEachRemaining(Consumer action):这是 Java 8为 Iterator 新增的默认方法,该方法可使用 Lambda 表达式来遍历集合元素。
public class IteratorTest {
    public static void main(String[] args) {
        // 创建一个集合
        Collection objs = new HashSet();
        objs.add("C语言中文网Java教程");
        objs.add("C语言中文网C语言教程");
        objs.add("C语言中文网C++教程");
        // 调用forEach()方法遍历集合
        // 获取books集合对应的迭代器
        Iterator it = objs.iterator();
        while (it.hasNext()) {
            // it.next()方法返回的数据类型是Object类型,因此需要强制类型转换
            String obj = (String) it.next();
            System.out.println(obj);
            if (obj.equals("C语言中文网C语言教程")) {
                // 从集合中删除上一次next()方法返回的元素
                it.remove();
            }
            // 对book变量赋值,不会改变集合元素本身
            obj = "C语言中文网Python语言教程";
        }
        System.out.println(objs);
    }
}

Collection

集合的几种遍历方式

   @Test
    public void testForeach() {
        Collection<String> collection = new ArrayList<>();
        collection.add("i");
        collection.add("love");
        collection.add("china");
        // foreach遍历
        collection.forEach(e-> System.out.println(e));
        // 可以使用方法引用简写
        collection.forEach(System.out::println);
        // 或者迭代器的forEachRemaining方法
       collection.iterator().forEachRemaining(System.out::println);
    }

List

  • ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
  • LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
  • Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素

Set

  • HashSet底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
  • LinkedHashSet底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。
  • TreeSet底层数据结构采用二叉树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。根据构造方法不同,分为自然排序(无参构造)和比较器排序(有参构造),自然排序要求元素必须实现Compareable接口,并重写里面的compareTo()方法,元素通过比较返回的int值来判断排序序列,返回0说明两个对象相同,不需要存储;比较器排需要在TreeSet初始化是时候传入一个实现Comparator接口的比较器对象,或者采用匿名内部类的方式new一个Comparator对象,重写里面的compare()方法;

Map

 

 

HashMap

  • 不是线程安全的,源码中没有与线程有关的任何内容
  • 有一个静态内部类Node,实现了Map.Entry接口,该类中包含key、key的hash值、value、下一个node的引用,HashMap中的每一个元素都是一个Node对象
  • 特殊的实例变量threshold:域值,指HashMap中的有效数据等于这个值的时候,就要进行扩容
  • 特殊的实例变量loadFactor:默认为0.75,指每次进行扩容后的threshold与可容纳的长度的比值
  • 储存数据时,是根据key的hash值作为数组的下标来保存,默认的初始化没有对HashMap设置容量大小,当第一次put数据时,会设置默认容量=16,即一个长度为16的数组,然后根据loadFactor得到域值(默认为16*0.75=12),即当数组中的实际个数大于12时,将数组的长度和域值一起进行扩容(<<1)为原来的两倍
  • 扩容后的数为2的幂次方,因为在put数据时,是根据hash值与长度求&(也就是取模)得到下标进行保存的,即容量length为2^n,hash&(length-1)==hash%length

最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为 Null。非同步的。

TreeMap

能够把它保存的记录根据键(key)排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。 

Hashtable

  • 是线程安全的,synchronized关键字只添加到了方法上,所以是相对的线程安全
  • 静态内部类为Entry实现了Map.Entry
  • 是数组+链表的结构
  • 默认的初始化大小为11,loadFactory为0.75,域值为11*0.75=8,且域值最大为Integer.MAX_VALUE-7,防止OOM
  • 在扩容后,为原来的2倍+1
  • value不允许为空

与 HashMap类似,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。 

LinkedHashMap

  • 不是线程安全
  • 继承了HashMap
  • 静态内部类为Entry也是继承自HashMap.Node,并多了两个引用before、after,表明为双向链表结构
  • 整体结构为链表+数组(hash表)+链表+红黑树,即除了与HashMap的结构相同外,还另外维护一个双向链表结构,该链表中内容为每个节点的前一个和后一个节点
  • 有一个成员属性boolean accessOrder,表示迭代顺序,默认为false(true为访问顺序,指根据访问的顺序进行排序;false为插入顺序,指根据插入的顺序进行排序)
  • 增加(put):用的还是HashMap中的方法,在HashMap的put()方法中调用了newNode()方法,该方法在HashMap中直接返回新的Node,LinkedHashMap重写了该方法,指定了Node的before和after,来保证有序
  • 在HashMap中有几个模板方法,实现是在LinkedHashMap中,实现内容即根据accessOrder对链表进行修改以保证对应顺序
  • 在进行containsValue时,是通过循环节点来实现,比HashMap通过下标循环取数快
  • 其他特点与HashMap一样,因为是继承自HashMap
  • 有一个特殊的算法:LRU(least recently used)最近最少使用算法,或距今最久未用淘汰算法,是缓存管理中的一个算法,其内容为将最近访问过的元素移动到链尾,链头的元素即为访问过时间最久的

保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。key和value均允许为空,非同步的。

ConcurrentHashMap

与HashMap的区别是什么?

ConcurrentHashMap是HashMap的升级版,HashMap是线程不安全的,而ConcurrentHashMap是线程安全。而其他功能和实现原理和HashMap类似。

与Hashtable的区别是什么?

Hashtable也是线程安全的,但每次要锁住整个结构,并发性低。相比之下,ConcurrentHashMap获取size时才锁整个对象。

Hashtable对get/put/remove都使用了同步操作。ConcurrentHashMap只对put/remove同步。

Hashtable是快速失败的,遍历时改变结构会报错ConcurrentModificationException。ConcurrentHashMap是安全失败,允许并发检索和更新。

posted @ 2022-09-28 17:49  happy_in  阅读(28)  评论(0)    收藏  举报