十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Java集合分类

Java集合分类 一. Java 集合:Collection 单列集合与 Map 双列集合1. Collection 单列集合每次存储单个元素只有值没有对应的键。例子脉劫、康帅傅、粤利粤每个元素独立存在。子接口List有序、可重复、有索引。代表ArrayList、LinkedListSet无序、不可重复、无索引。代表HashSet、TreeSet2. Map 双列集合存储键值对key-value一组数据包含键 (key) 和值 (value) 两部分。key键唯一不可重复value值可以重复常用实现类HashMap、LinkedHashMap、TreeMap二. Collection 单列集合一、集合对比总表List 系列有序、可重复、有索引集合底层结构特点适合场景ArrayListObject动态数组有序、可重复、有索引查询快中间增删慢JDK8懒加载首次add容量10扩容1.5倍查询多中间增删少LinkedList双向链表有序、可重复、有索引查询慢首尾增删极快无扩容机制结点内存不连续频繁对首尾增删查询少Set 系列不可重复集合底层结构特点适合场景HashSet哈希表数组链表/红黑树无序、不可重复、无索引依靠hashCode()equals()去重默认容量16加载因子0.75扩容2倍只去重不关心顺序LinkedHashSet哈希表双向链表插入有序、不可重复、无索引哈希表去重链表记录插入顺序需要去重同时保留存入顺序TreeSet红黑树不可重复、自然排序、无索引元素必须实现Comparable接口自动从小到大排序去重二、List集合考点List共同特点有序、可重复、有索引。List独有4个索引方法void add(int index,E element)在指定位置插入元素E remove(int index)删除指定索引元素返回被删元素E set(int index,E element)修改指定索引元素返回旧元素E get(int index)获取指定索引元素ArrayList底层底层Object[] elementDatasize是实际元素个数不等于数组length。JDK8无参构造创建空数组第一次add才初始化容量10扩容为原容量1.5倍底层Arrays.copyOf拷贝数组。数组查询快中间增删慢需要移动大量元素。LinkedList底层底层双向链表结点保存【前驱地址值后继地址】内存不连续,而且占内存。特点查询慢首尾增删极快,增删相对快。特有首尾方法addFirst、addLast、getFirst、getLast、removeFirst、removeLast。没有数组没有扩容。get(index)会就近遍历查询效率低。三、Set集合考点Set共同特点不可重复没有索引不能使用普通for循环(fori)遍历。✅ Set可用遍历方式① 迭代器 Iterator② 增强for循环 for-each底层是迭代器③ Lambda forEachJDK8⚠️ 增强for、Lambda forEach遍历中不能直接用集合的add/remove会并发修改异常边遍历边删除只能用迭代器it.remove()HashSet底层原理底层哈希表数组链表红黑树JDK8,之前是数组链表。默认容量16加载因子0.75(平衡哈希冲突与内存开销)超过阈值扩容为2倍,如:扩容阈值 容量 * 加载因子元素个数超过阈值16*0.7512就按两倍扩容。去重规则先调用hashCode()得到哈希值计算数组下标下标位置有元素时哈希值不同直接挂链表哈希值相同调用equals()对比内容equals返回true判定重复不存储。自定义对象存入HashSet必须重写hashCode()和equals()否则无法去重。链表长度≥8数组≥64链表转红黑树,提升查询速度红黑树节点数量≤6红黑树退化成链表,减少维护红黑树开销。为什么不选平衡二叉树平衡二叉树旋转次数多红黑树牺牲一部分严格平衡减少旋转综合效率更高。LinkedHashSetHashSet子类哈希表做去重双向链表记录插入顺序存入和取出顺序一致。TreeSet底层红黑树不可重复、自动排序、无索引,元素自动排序存入元素需要实现Comparable接口。⚠️ 不是存入顺序是元素本身大小的自然排序。去重规则根据元素的比较结果判断重复不再依赖 hashCode 和 equals。如果两个元素比较返回 0 → 判定为重复不存入集合。注意 :HashSet存入自定义对象成员变量值相同却不能去重原因自定义类默认继承 Object 的 hashCode() 和 equals() 方法Object.hashCode()基于对象地址生成哈希值 ,而当new了一个对象之后,两个对象地址不同 → 默认hashCode不同直接判定为不同对象存入集合不会去重。Object.equals()底层比较对象地址不比较对象里面的属性值HashSet存自定义对象,如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode() and equals()方法,按属性判断重复补充hashCode与equals的关系面试必背hashCode相等对象不一定相等存在哈希碰撞hashCode不相等对象一定不相等HashSet去重规则① 先判断hashCodehashCode不同直接判定不同对象② hashCode相同再调用equals比较内容③ equals返回true才判定为重复元素拒绝存入规范重写equals()必须同时重写hashCode()保证属性相同的对象hashCode结果也相同。四、并发修改异常 ConcurrentModificationException触发迭代器遍历集合时直接调用集合的add/remove修改集合,出异常。解决方案① List有索引普通for循环删除后 i - -(因为删除元素之后后一个元素会补位)或者倒序遍历删除② List / Set通用迭代器遍历使用it.remove()删除注意增强for循环、Lambda forEach只适合读取遍历遍历中不能增删会报并发修改异常五、迭代器 IteratorhasNext()判断是否还有下一个元素返回booleannext()取出下一个元素指针后移it.remove()删除刚刚next拿到的元素同步更新modCount不会并发修改异常必须先next才能remove三 .Map双列集合1 . Map概述Map是双列集合一次存储一对键值对keyvaluekey唯一、不可重复key重复会覆盖旧valuevalue可重复2. Map常用实现类HashMap底层哈希表key无序不重复LinkedHashMap哈希表双向链表key存取有序不重复TreeMap红黑树key自动排序不重复3. Map常用方法put(K key,V value)添加键值对key存在则覆盖旧valueget(Object key)根据key获取value找不到返回nullremove(Object key)按key删除返回被删除valuecontainsKey(key)判断是否存在该keycontainsValue(value)判断是否存在该valuekeySet()获取全部key返回Set集合entrySet()获取全部键值对对象Entry4. Map两种遍历keySet遍历获取所有key循环key调用get拿valueSetStringkeysmap.keySet();for(Stringkey:keys){System.out.println(key : map.get(key));}2.entrySet 遍历推荐效率更高Map.Entry 代表单个键值对对象可以 getKey ()、getValue ()SetMap.EntryString,IntegerentrySetmap.entrySet();for(Map.EntryString,Integerentry:entrySet){Stringkentry.getKey();Integerventry.getValue();}Lambda forEachJDK1.8 新增代码最简map.forEach((k,v)-System.out.println(kv));Map底层原理1.HashMap 底层原理重点底层哈希表数组 单向链表 红黑树HashSet 底层就是 HashMapHashSet 存入的元素就是 HashMap 的 keyvalue 是固定占位对象。存入流程key 调用 hashCode 计算哈希值定位数组下标同一下标位置对比 keyhashCode 相同 equals 返回 true → key 重复覆盖旧 valuehashCode 相同 equals 返回 false → 挂链表JDK8 树化规则链表长度≥8数组长度≥64链表转为红黑树提高查询速度红黑树节点≤6退化为单向链表扩容参数默认容量16加载因子 0.75阈值 容量 * 0.75到达阈值数组扩容 2 倍。自定义对象作为 HashMap 的 key必须重写 hashCode () 和 equals ()2. LinkedHashMap底层哈希表 双向链表key 存取有序去重规则同 HashMap。3.TreeMap底层红黑树key 自动排序排序与去重依靠 Comparable/Comparator不依赖 hashCode 和 equals。比较返回 0判定 key 重复覆盖 value。HashMap 面试考点HashMap 允许一个 null 的 keyvalue 允许多个 nullTreeMap 的 key 不能为 null。HashMap线程不安全多线程推荐 ConcurrentHashMap。HashMap vs HashtableHashMapJDK1.2线程不安全key 可以 null默认容量 16扩容 ×2HashtableJDK1.0方法加 synchronized线程安全key 不能 null默认容量 11扩容 ×21四 . Stream 流一.认识 Stream 流Stream 叫做流,是 JDK8 新特性用来对集合、数组进行数据处理的流水线。不是集合,不存储数据只是对数据源的数据做加工,结合了Lambda 表达式。作用简化集合 / 数组的遍历、过滤、排序、去重、统计等操作代码更简洁不用手写大量 for 循环。步骤 : ①获取 Stream 流 → ②中间操作过滤、排序、去重 → ③终结操作得到结果1.获取 Stream 流Stream 流代表一条流水线和数据源建立连接,得到流对象数据源可以是Collection集合、数组// 集合获取流ListStringlistnewArrayList();StreamStringstreamlist.stream();// 数组获取流String[]arr{西湖,乌镇};StreamStringstream2Arrays.stream(arr);✅ 作用只是建立管道还没有真正处理数据2. 中间操作流水线过滤、排序、去重中间操作 :链式调用方法处理数据特点延迟执行不触发计算只是把操作保存起来filter()过滤sorted()排序distinct()去重limit()取前 n 个skip()跳过前 n 个3. 获取结果终结操作触发流水线执行一旦执行流就关闭不能再用collect()收集到新集合List/Setcount()统计元素个数forEach()遍历publicclassStreamDemo{publicstaticvoidmain(String[]args){ListStringlistnewArrayList();list.add(西湖);list.add(乌镇);list.add(西湖);list.add(西塘);// Stream三步ListStringresultlist.stream()//1. 获取流建立流水线.distinct()//2.中间操作去重.filter(s-s.length()2)//2.中间操作过滤保留2个字景点.collect(Collectors.toList());//3.终结操作收集成新ListSystem.out.println(result);}}中间操作不会执行只有遇到终结方法整条流水线才运行Stream 流不能复用终结之后流就关闭不能再次调用Stream 不会修改原来的集合数据源处理结果放到新集合对比原来的普通 for 循环 VS Stream 流for 循环自己写遍历、if 判断、创建集合代码长Stream链式调用一行完成过滤、去重、收集代码简洁五 . Collections 工具类方法名称说明public static T booleanaddAll(Collection? super T c, T… elements)给集合批量添加元素public static voidshuffle(List? list)打乱List集合中的元素顺序public static T voidsort(ListT list)对List集合中的元素进行升序排序public static T void sort(ListT list, Comparator? super T c)对List集合中元素按照比较器规则排序1.Collections和Collection区别Collection 是集合顶层接口List、Set 的父接口Collections 是集合工具类提供静态方法操作集合对象。2.shuffle 只能作用于List 集合用来随机打乱元素顺序。3.sort(List list)默认自然升序sort(List list, Comparator)自定义比较规则排序支持 Lambda。六 . subList 集合复制 浅拷贝深拷贝1. List—subList 集合截取核心list.subList(fromIndex, toIndex)区间左闭右开[from, to)包含起始索引不包含结束索引返回值原集合的视图不是全新 ArrayListsubList 没有新建数组底层还是原 List。修改子集合元素set/remove原集合同步变化如果原集合发生结构性修改add/remove再操作 subList抛出 ConcurrentModificationException参数不能负数toIndex 不能大于 list.size ()否则抛IndexOutOfBoundsExceptionsubList(2,2) 返回空集合不报错// 推荐生成全新List和原list互不影响ListStringnewListnewArrayList(list.subList(1,3));ListStringsublist.stream().skip(1)// 跳过前1个.limit(2)// 取2个元素.collect(Collectors.toList());Stream 方式返回新集合无视图陷阱性能略低于 subList。一句话记忆subList 是视图不是新集合想安全就包一层 new ArrayList。集合复制一个集合倒入另一个集合✅方式 1构造方法新建目标集合// 新建target把source全部元素倒入ListUsertargetnewArrayList(source);方式 2addAll目标集合已存在追加元素ListUsertargetnewArrayList();target.addAll(source);错误写法不是拷贝只是引用赋值:ListUsertargetsource;// target和source指向同一个集合增删任意一个两边都会变重点new ArrayList(source) / addAll() 都是浅拷贝只是新建集合容器集合里面存放的对象还是同一个引用。修改对象属性源集合对象同步变化。浅拷贝集合转为深拷贝集合stream map 逐个新建对象:ListUsertargetsource.stream().map(oldUser-{UsernewUsernewUser();newUser.setId(oldUser.getId());newUser.setName(oldUser.getName());returnnewUser;}).collect(Collectors.toList());浅拷贝创建新外层对象基本类型复制值引用类型只复制对象地址新旧对象共用同一个子对象。修改子对象属性原对象跟着变。深拷贝创建全新外层对象并且递归新建所有引用类型子对象。新旧对象完全独立修改任意一方内部对象互不影响。1.subList 返回原集合视图不是新集合原集合结构性修改会抛并发异常想安全拷贝需要new ArrayList(subList)。2.new ArrayList(source) 和 addAll 是浅拷贝新建集合容器但元素对象共用引用。3.list.sort 原地修改原集合stream.sorted 返回新集合原集合不变。4.Comparable 是类自带比较规则Comparator 是临时比较器推荐 Lambda 写法。5.浅拷贝新建外层对象引用成员共享深拷贝所有层级对象全部新建完全隔离。6.集合浅拷贝陷阱集合增删互不影响但是修改集合里面对象属性源集合对象同步变化。问User u2 u1 是浅拷贝吗不是。只是引用赋值没有创建新对象两个引用指向同一个对象。浅拷贝一定会创建新外层对象。问浅拷贝集合中 target.setAddress(new Address()) 修改引用原对象会变吗不会。只是把拷贝对象的引用指向新对象。只有修改同一个子对象内部属性时才会互相影响。
返回列表