JAVA中集合框架

目录

目录

1.1.List

1.1.1.ArrayList

1.1.2.LinkedList

1.2.Set

1.2.1.HashSet

1.2.2.TreeSet

2.Map

2.1.HashMap

2.2.TreeMap

1.Collection

1.1.List

ArrayList和LinkedList的区别?

        ArrayList底层使用的是Object数组。由于数据存储是连续的,所以可以通过索引访问。造成数据处理查询快,增删慢的原因是数据存储是连续存储。线程不安全。

        LinkedList底层使用的是双向链表的数据结构。数据处理查询慢,增删快的原因是数据存储是不连续存储。线程不安全

1.1.1.ArrayList

        ArrayList自动扩充机制:

        实现机制:ArrayList.ensureCapacity(int minCapacity)
        首先得到当前elementData 属性的长度oldCapacity。
        然后通过判断oldCapacity和minCapacity参数谁大来决定是否需要扩容, 如果minCapacity大于 oldCapacity,那么我们就对当前的List对象进行扩容。 扩容的的策略为:取(oldCapacity * 3)/2 + 1和minCapacity之间更大的那个。然后使用数组拷 贝的方法,把以前存放的数据转移到新的数组对象中 如果minCapacity不大于oldCapacity那么就不进行扩容。

        当以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。当插入的元素个数大于当前容量时,就需要进行扩容了, ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右。

import java.util.ArrayList;
import java.util.Iterator;

public class ArrayListDemo {
    public static void main(String[] srgs) {
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        System.out.printf("Before add:arrayList.size() =% d\n", arrayList.size());
        arrayList.add(1);
        arrayList.add(3);
        arrayList.add(5);
        arrayList.add(7);
        arrayList.add(9);
        System.out.printf("After add:arrayList.size() =% d\n", arrayList.size());
        System.out.println("Printing elements of arrayList");
        // 三种遍历方式打印元素
        // 第一种:通过迭代器遍历
        System.out.print("通过迭代器遍历:");
        Iterator<Integer> it = arrayList.iterator();
        while (it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();
        // 第二种:通过索引值遍历
        System.out.print("通过索引值遍历:");
        for (int i = 0; i < arrayList.size(); i++) {
            System.out.print(arrayList.get(i) + " ");
        }
        System.out.println();
        // 第三种:for循环遍历
        System.out.print("for循环遍历:");
        for (Integer number : arrayList) {
            System.out.print(number + " ");
        }
        // toArray用法
        // 第一种方式(最常用)
        Integer[] integer = arrayList.toArray(new Integer[0]);
        // 第二种方式(容易理解)
        Integer[] integer1 = new Integer[arrayList.size()];
        arrayList.toArray(integer1);
        // 抛出异常,java不支持向下转型
        //Integer[] integer2 = new Integer[arrayList.size()];
        //integer2 = arrayList.toArray();
        System.out.println();
        // 在指定位置添加元素
        arrayList.add(2, 2);
        // 删除指定位置上的元素
        arrayList.remove(2);
        // 删除指定元素
        arrayList.remove((Object) 3);
        // 判断arrayList是否包含5
        System.out.println("ArrayList contains 5 is: " +
                arrayList.contains(5));
        // 清空ArrayList
        arrayList.clear();
        // 判断ArrayList是否为空
        System.out.println("ArrayList is empty: " + arrayList.isEmpty());
    }
}

1.1.2.LinkedList

        利用LinkedList实现栈(stack)、队列(queue)、双向队列(double-ended queue )。 它具有方法addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等。

队列(queue):

  1. 队列(Queue)是限定所有的插入只能在表的一端进行,而所有的删除都在表的另一端进行的线性表。
  2. 表中允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)。
  3. 队列的操作是按先进先出(FIFO)的原则进行的。
  4. 队列的物理存储可以用顺序存储结构,也可以用链式存储结构。

栈(stack):

  1. 栈(Stack)也是一种特殊的线性表,是一种后进先出(LIFO)的结构。
  2. 栈是限定仅在表尾进行插入和删除运算的线性表,表尾称为栈顶(top),表头称为栈底(bottom)。
  3. 栈的物理存储可以用顺序存储结构,也可以用链式存储结构。

1.2.Set

1.2.1.HashSet

1.2.2.TreeSet

2.Map

HashMap和Hashtable的区别
回答:

(1)线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);

(2)对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;HashTable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。

(3)初始容量大小和每次扩充容量大小的不同 : ① 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。② 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小(HashMap 中的tableSizeFor()方法保证,下面给出了源代码)。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。

(4)底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。

(5)效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;

HashMap的常用方法

import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<String, String>();
        // 键不能重复,值可以重复
        map.put("san", "张三");
        map.put("si", "李四");
        map.put("wu", "王五");
        map.put("wang", "老王");
        map.put("wang", "老王2");// 老王被覆盖
        map.put("lao", "老王");
        System.out.println("-------直接输出hashmap:-------");
        System.out.println(map);
        /**
         * 遍历HashMap
         */
        // 1.获取Map中的所有键
        System.out.println("-------foreach获取Map中所有的键:------");
        Set<String> keys = map.keySet();
        for (String key : keys) {
            System.out.print(key+"  ");
        }
        System.out.println();//换行
        // 2.获取Map中所有值
        System.out.println("-------foreach获取Map中所有的值:------");
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.print(value+"  ");
        }
        System.out.println();//换行
        // 3.得到key的值的同时得到key所对应的值
        System.out.println("-------得到key的值的同时得到key所对应的值:-------");
        Set<String> keys2 = map.keySet();
        for (String key : keys2) {
            System.out.print(key + ":" + map.get(key)+"   ");
        }
        /**
         * 如果既要遍历key又要value,那么建议这种方式,应为如果先获取keySet然后再执行
         map.get(key),map内部会执行两次遍历。
         * 一次是在获取keySet的时候,一次是在遍历所有key的时候。
         */
        // 当我调用put(key,value)方法的时候,首先会把key和value封装到
        // Entry这个静态内部类对象中,把Entry对象再添加到数组中,所以我们想获取
        // map中的所有键值对,我们只要获取数组中的所有Entry对象,接下来
        // 调用Entry对象中的getKey()和getValue()方法就能获取键值对了
        Set<java.util.Map.Entry<String, String>> entrys = map.entrySet();
        for (java.util.Map.Entry<String, String> entry : entrys) {
            System.out.println(entry.getKey() + "--" + entry.getValue());
        }

        /**
         * HashMap其他常用方法
         */
        System.out.println("after map.size():"+map.size());
        System.out.println("after map.isEmpty():"+map.isEmpty());
        System.out.println(map.remove("san"));
        System.out.println("after map.remove():"+map);
        System.out.println("after map.get(si):"+map.get("si"));
        System.out.println("after map.containsKey(si):"+map.containsKey("si"));
        System.out.println("after containsValue(李四):"+map.containsValue("李四"));
        System.out.println(map.replace("si", "李四2"));
        System.out.println("after map.replace(si, 李四2):"+map);
    }
}

HashMap和TreeMap的区别?
回答:

1、HashMap是通过hash值进行快速查找的;HashMap中的元素是没有顺序的;TreeMap中所有的元素都是有某一固定顺序的,如果需要得到一个有序的结果,就应该使用TreeMap;

2、HashMap和TreeMap都是线程不安全的;

3、HashMap继承AbstractMap类;覆盖了hashcode() 和equals() 方法,以确保两个相等的映射返回相同的哈希值;

TreeMap继承SortedMap类;他保持键的有序顺序;

4、HashMap:基于hash表实现的;使用HashMap要求添加的键类明确定义了hashcode() 和equals() (可以重写该方法);为了优化HashMap的空间使用,可以调优初始容量和负载因子;

TreeMap:基于红黑树实现的;TreeMap就没有调优选项,因为红黑树总是处于平衡的状态;

5、HashMap:适用于Map插入,删除,定位元素;

TreeMap:适用于按自然顺序或自定义顺序遍历键(key)
 

2.1.HashMap

HashMap的底层实现?扩容?是否线程安全?

  1. 在jdk1.7之前HashMap是基于数组和链表实现的,而且采用头插法。
  2. 在jdk1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。采用尾插法。

  1. HashMap默认的初始化大小为 16。当HashMap中的元素个数之和大于负载因子*当前容量的时候就要进行扩充,容量变为原来的 2 倍。(这里注意不是数组中的个数,而且数组中和链/树中的所有元素个数之和!)

注意:我们还可以在预知存储数据量的情况下,提前设置初始容量(初始容量 = 预知数据量 / 加载因子)。这样做的好处是可以减少 resize() 操作,提高 HashMap 的效率。

HashMap是线程不安全的,其主要体现:

1.在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。

2.在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。

HashMap扩容的时候为什么是2的n次幂?
        回答:数组下标的计算方法是(n-1)& hash,取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。并且采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。


HashMap的put方法说一下。
        回答:通过阅读源码,可以从jdk1.7和1.8两个方面来回答

  1. 根据key通过哈希算法与与运算得出数组下标
  2. 如果数组下标元素为空,则将key和value封装为Entry对象(JDK1.7是Entry对象,JDK1.8是Node对象)并放入该位置。
  3. 如果数组下标位置元素不为空,则要分情况

    (i).如果是在JDK1.7,则首先会判断是否需要扩容,如果要扩容就进行扩容,如果不需要扩容就生成Entry对象,并使用头插法添加到当前链表中。

    (ii).如果是在JDK1.8中,则会先判断当前位置上的TreeNode类型,看是红黑树还是链表Node

    (a).如果是红黑树TreeNode,则将key和value封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value。

    (b).如果此位置上的Node对象是链表节点,则将key和value封装为一个Node并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历过程中会判断是否存在当前key,如果存在则更新其value,当遍历完链表后,将新的Node插入到链表中,插入到链表后,会看当前链表的节点个数,如果大于8,则会将链表转为红黑树

    (c).将key和value封装为Node插入到链表或红黑树后,在判断是否需要扩容,如果需要扩容,就结束put方法。

HashMap源码中在计算hash值的时候为什么要右移16位?

        回答:我太难了!

2.2.TreeMap

3.集合中线程安全

Java中线程安全的集合有哪些?

  1. Vector:就比Arraylist多了个同步化机制(线程安全)。
  2. Hashtable:就比Hashmap多了个线程安全。
  3. ConcurrentHashMap:是一种高效但是线程安全的集合。
  4. Stack:栈,也是线程安全的,继承于Vector。


ConcurrentHashMap的底层实现,它为什么是线程安全的?
        回答:在jdk1.7是 分段的数组+链表 ,jdk1.8的时候跟HashMap1.8的时候一样都是基于数组+链表/红黑树。

ConcurrentHashMap是线程安全的

  1. 在jdk1.7的时候是使用分段所segment,每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
  2. 在jdk1.8的时候摒弃了 Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。synchronized只锁定当前链表或红黑二叉树的首节点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 集合框架主要分为两个部分:`java.util` 和 `java.util.concurrent`。其,`java.util` 包含了大部分的集合框架,而 `java.util.concurrent` 则包含了一些并发集合框架。 在 `java.util` 包集合框架主要分为两类:`Collection` 和 `Map`。`Collection` 接口表示一组对象,它是所有集合框架的基础接口,提供了对集合元素的基本操作,例如添加、删除、查找等。`Map` 接口则表示一组键值对,提供了根据键来查找值的操作。 在 `Collection` 接口下,Java 常用的集合框架包括: 1. `List`:有序列表,元素可以重复。常用的实现类有 `ArrayList`、`LinkedList` 和 `Vector`。 2. `Set`:无序集合,元素不可以重复。常用的实现类有 `HashSet`、`LinkedHashSet` 和 `TreeSet`。 3. `Queue`:队列,元素按照一定的顺序进行排列,常用的实现类有 `LinkedList` 和 `PriorityQueue`。 4. `Deque`:双端队列,可以在队列的两端进行插入和删除操作,常用的实现类有 `ArrayDeque` 和 `LinkedList`。 在 `Map` 接口下,Java 常用的集合框架包括: 1. `HashMap`:无序键值对集合,键不可以重复。 2. `LinkedHashMap`:有序键值对集合,键不可以重复。 3. `TreeMap`:有序键值对集合,键可以按照自然顺序进行排序。 4. `Hashtable`:与 `HashMap` 类似,但是是线程安全的,不推荐使用。 总之,Java 集合框架提供了丰富的数据结构和算法,可以大大简化程序的开发,提高程序的效率和可维护性。需要根据具体的需求来选择合适的集合框架和实现类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值