Java 汇合是一个罕用的技术 ,无论是在: 开发应用,还是面试总是高频的提及到~
- 正因如此: 本篇是对集体应用汇合的, 总结 办法...
不适宜初学者,适宜面试|温习...
Java 汇合
汇合和数组:
- 数组申明了它包容的元素的类型,而汇合能够不申明
存储Object类型
能够通过泛型进行标准! - 数组是动态的,一个数组实例具备固定的大小,一旦创立了就无奈扭转容量了。
汇合是能够动静扩大容量,能够依据须要动静扭转大小,汇合提供更多的成员办法,能满足更多的需要。 - 汇合: 和数组一样Java中用来存储数据的作用,
补救了数组长度固定的毛病更灵便
Java 汇合框架概述
Java 汇合可分为 Collection 和 Map 两种体系
Collection接口: 单列数据,定义了存取一组对象的办法的汇合
- List:元素有序、可反复的汇合
- Set:元素无序、不可反复的汇合
Map接口:双列数据,保留具备映射关系 key-value
的汇合
Collection 接口:
简介
Collection汇合次要有List和Set两大接口 不罕用的 Queue接口
List 接口
- 一个有序,(元素存入汇合的程序和取出的程序统一) 的汇合容器
元素能够反复,能够插入多个null元素,元素都有索引
- 有三个实现类:
ArrayList
LinkedList
Vector
Set 接口
一个无序, (存入和取出程序有可能不统一) 的汇合容器
不能够存储反复元素,只容许存入一个null元素
必须保障元素唯一性
- 罕用实现类:
HashSet
LinkedHashSet
TreeSet
Collection接口的罕用办法:
JDK不提供此接口的任何间接实现,而是提供更具体的子接口:set
list
都默认继承了Collection 的办法();
boolean .add( object ); //在列表 开端添元素 和数组一样 起始索引地位从 0 开使,可储存null; 数值类型会主动装箱;void .add( int,object ); //指定索引地位增加元素 但 不可指定 在超过目前 汇合长度;void .forEach(System.out::print);//jdk8 新个性;一种循环遍历; void .clear(); //清空集合;int .size(); //返回汇合元素个数; boolean .contains( object ); //判断汇合在是否存在指定元素; ture/flase; 底层相当于是调 equls();比,String类重写了所以比拟的是值; //没重写当然就是比拟地址咯;boolean .containAll( Collection ); //判断参数是个汇合,判断参数汇合本人有没有,都有就true 一个没有就falseboolean .equals( Coll... ); //想要返回 true,须要以后汇合元素 和 参数汇合元素值都雷同 地址 ;依据new ArrayList 或其它汇合/其程序也要雷同;Object .get( int ); //返回指定索引的元素;( 留神这是Object 类型须要类型转换 );List .sublist(int1,int2); //返回汇合 1-2 地位的子集合;Object .set(int,Object); //设置指定 int 地位的元素;留神不能够在,汇合进行循环遍历的时候移除元素! 因为汇合长度发送扭转,循环遍历报错!Object .remove( int ); //删除指定下标中的元素; 返回删除的元素;boolean .remove( object/int ); //移除汇合中指定元素; true移除胜利 false移除失败:不存在要移除元素; //尽管数值类型会装箱,int下标删除了..须要确保下标存在boolean .removeAll( Collection ); //从汇合中移除指定: 一个汇合元素; 两个汇合A B, A.removeAll(B); //把A B汇合中一样的元素移除掉了(交加);正确的删除须要应用:Iterator对象~Iterator .iterator(); //返回一个 Iterator对象; 数组汇合互相转换Object[] .toArray(); //能够将汇合返回一个 Object[] 数组;汇合 Arrays.aslist( 数组 ); //Arrays类的静态方法: 将数组转换成汇合;
List 接口
Arraylist
Arraylist: 对数组进行封装,实现长度可变的数组(依据增删主动扭转元素在汇合中地位)
Arraylist
底层其实就是一个,能够动静调节长度的数组
- JDK1.7:ArrayList饿汉式,间接创立一个初始容量为10的数组
- JDK1.8:ArrayList懒汉式,一开始创立一个长度为0的数组,当增加第一个元素时再创立一个始容量为10的数组
- 优: 更利于遍历 和随机拜访元素
根本应用:
- 开发罕用的就是这个,这里就以 Arraylist 举例了
public void jh(){ /** 创立汇合对象 */ // Collection coll = new ArrayList(); ArrayList arrayList = new ArrayList(); //List汇合存储Object类型元素,反对反复元素! arrayList.add(1); arrayList.add(2); arrayList.add(2); arrayList.add(3); arrayList.add("2"); arrayList.add(null); //能够存储null System.out.println("arrayList汇合遍历:"); for (Object o : arrayList) { System.out.println(o); } /** 罕用办法 */ //contains(Object obj):判断以后汇合中是否蕴含obj :判断时会调用obj对象所在类的equals(),自定义对象须要重写equals System.out.println("arrayList汇合是否存在 1:"+arrayList.contains(1)); //containsAll(Collection coll1) 判断形参coll1中的所有元素是否都存在于以后汇合中 ArrayList arrayList2 = new ArrayList(); arrayList2.add(1); arrayList2.add(2); System.out.println("arrayList汇合是否存在 arrayList2的所有元素:"+arrayList.containsAll(arrayList2)); //retainAll(Collection coll1):交加:获取以后汇合和coll1汇合的交加,并返回给以后汇合 // arrayList.retainAll(arrayList2); // System.out.println("retainAll(Collection coll1):交加:获取以后汇合和coll1汇合的交加,并返回给以后汇合"); // for (Object o : arrayList) { // System.out.println(o); // } /** 正文下面: retainAll() 让两个汇合元素 A.retainAll(B) A汇合只保留B 交加独特的数据了~ */ //remove(Object obj):从以后汇合中移除obj元素 //removeAll(Collection coll1):差集:从以后汇合中移除coll1中所有的元素 arrayList.removeAll(arrayList2); System.out.println("removeAll(Collection coll1):差集:从以后汇合中移除coll1中所有的元素"); for (Object o : arrayList) { System.out.println(o); } /** 数组 ———》List汇合 */ System.out.println("应用: Arrays. asList(array) 进行转换"); List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"}); System.out.println(list); /** List汇合 ———》数组 */ System.out.println("应用: List 自带的 toArray() 办法"); Object[] objects = list.toArray(); for (Object obj : objects) { if(obj instanceof String) // 对象 instanceof 类型 比拟对象是否属于改类型~ System.out.print(obj+" "); }}
LinkedList
LinkedList:采纳双向链表存储形式
优:
在插入删除时效率更高,同Arraylist一样都实现list接口比Arraylist多了一些办法
毛病就是查找遍历没有Arraylist 快
LinkedList 新增办法
void .addFist( object ); //在汇合 首部 增加元素 (索引0);void .addLast( object ); //在汇合 开端 增加元素;Object .getFist( ); //返回 第一元素;Object .getLast( ); //返回 最初一个元素; Object .removrFist( ); //删除 并 返回第一个元素;Object .removeLast( ); //删除 并 返回最初一个元素;
Vector
Vector 读:歪课特
远古代码,jdk1.0就有的
通过 Vector(); 结构器创建对象:
底层就创立了长度为10的数组,扩容方面就是扩大为原数组长度的二倍;
ArrayList/LinkedList/vector的异同?
ArrayList和LinkedList的异同
- 二者都是线程不平安的,绝对于线程平安的Vector,
执行效率高
- ArrayList是实现了基于动静数组的数据结构,LinkedList是基于链表的数据结构
- 对于随机拜访get和set:
ArrayList优于LinkedList, LinkedList要挪动指针
- 对于新增(特质插入)add和删除remove操作:
LinkedList占优,因为ArrayList要挪动数据
ArrayList和Vector的异同:
- Vector和ArrayList简直是完全相同的
- Vector是同步类(Synchronized),强同步类:
开销就比ArrayList大,拜访要慢~
- Vector每次扩容申请其大小的2倍空间,而ArrayList是1.5倍
Set 接口
简介:
Set接口实现 Collection接口
- 存储一组
无序的
(不等于随机,而是依据数据的哈希值决定的),不可反复
的,汇合数据
- Set接口中没有额定定义新的办法,应用的都是Collection 中申明过的办法:
- Set 接口的实现类:
HashSet
LinkedHashSet
TreeSet
HashSet
惟一,无序。基于 HashMap 实现,底层采纳 HashMap 保留数据
它不容许汇合中有反复的值,
将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()办法,这样能力比拟对象的值是否相等,以确保set中没有贮存相等的对象
LinkedHashSet: 作为HashSet子类,遍历器外部数据时,能够依照增加的程序遍历
- 作为HashSet类的子类,在增加数据同时,每个数据还保护两个援用,
记录了此数据前一个数据和后一个数据
- 优:频繁的遍历操作,效率高
ThreeSet
- 有序,惟一): 红黑树(自均衡的排序二叉树
向TreeSet增加的数据,要求是雷同的类型的
自定义类须要:
指定排序规定
天然排序: 定义类继承 Comparable 重写 compareTo(obj);
定制排序: TreeSet在new 时就传入一个 Comparator类对象
List数据去重
五种无效办法:
- [计划一:借助HashSet的个性进行去重]
- [计划二 : 利用set汇合个性放弃程序统一去重]
TreeSet
LinkedHashSet
- [计划三 : 应用list本身办法remove()–>不举荐]
循环遍历判断,remove()
- [计划四 : 应用Java8个性去重]
/** 计划一 */@org.junit.Test public void distinct() { List<String> lists = Arrays.asList(new String[]{"AA", "BB", "CC","CC"}); HashSet<String> sets = new HashSet<>(); for (String s : lists) { sets.add(s); } System.out.println("去重后的Set汇合: "+sets);}@org.junit.Test /** 计划二 */ public void distinct2() { List<String> lists = Arrays.asList(new String[]{"AA", "BB", "CC","CC"}); //形式一 List<String> listNew = new ArrayList<String>(new TreeSet<String>(lists)); System.out.println("TreeSet"+listNew); //形式二 List<String> listNew2 = new ArrayList<String>(new LinkedHashSet<String>(lists)); System.out.println("LinkedHashSet"+listNew);}@org.junit.Test /** 计划四 */ public void distinct4() { List<String> lists = Arrays.asList(new String[]{"AA", "BB", "CC","CC"}); List<String> myList = lists.stream().distinct().collect(Collectors.toList()); System.out.println("JDK8.0新个性:"+myList);}
Iterator 迭代器接口
Iterator 中:迭代器 读:艾特瑞特~
能够通过,Collection类的 .iterator()
办法返回一个 Iterator迭代器对象
- Map中存在keySet()办法返回Set类型对象,Set接口实现Collection 因而间接存在 iterator(); 办法....
Iterator接口的办法
boolean hasNext(); //判断是否 存在下一个可拜访的元素;Object next(); //返回要拜访的下一个元素;void remove(); //移除以后的汇合元素可保障从源汇合中平安地删除对象; 留神(图:Iterator移除)
- 迭代器是通过 汇合
.iterator() 办法获取
示意对该汇合的迭代 - 迭代器存在 游标的概念:
默认游标都在汇合的第一个元素之前
,因而:通过Next(); 获取下一个元素!留神: 在调用it.next()办法之前必须要调用it.hasNext()进行检测
若不调用,且下一条记录有效间接调用
i.next()
会抛出NoSuchElementException异样。
Iterator 仅用于遍历汇合
边遍历边批改 汇合 的惟一正确形式是应用 Iterator.remove() 办法,如下:
Iterator<Integer> it = list.iterator();while(it.hasNext()){ *// do something* it.remove();}
一种最常见的错误代码如下:
for(Integer i : list){ list.remove(i)}
- 运行以上错误代码会报
ConcurrentModificationException 异样
- 这是因为当应用 foreach(for(Integer i : list)) 语句时,会主动生成一个iterator 来遍历该 list
- 但同时该 list 正在被 .remove() 批改。Java 个别不容许一个线程在遍历 Collection 时另一个线程批改它
Map 接口:
简介
双列汇合,存储一对 key-value 数据---》
- K键:无序 惟一
- V值:无序 反复;
HashMap:作为Map的次要实现类;线程不平安的,效率高;容许存储null的key和value
LinkedHashMap:保障在遍历map元素时,能够依照增加的程序实现遍历
起因: 在原有的HashMap底层构造根底上,增加了一对指针,指向前一个和后一个元素
对于频繁的遍历操作,此类执行效率高于HashMap
TreeMap:保障依照增加的key-value对进行排序,实现排序遍历。此时思考key的天然排序或定制排序 底层应用红黑树
Hashtable:作为古老的实现类
- 线程平安的,效率低;不能存储null的key和value
- Properties: 罕用来解决配置文件。
key和value都是String类型
实现类有:
HashMap
LinkedHasMap
TreeMap
Hashtable(jdk1.0)
Properties
Map罕用办法:
Object .put(Object key,Object value); //以 键值对 形式存储 留神(键 惟一 值 能够反复 //雷同的键 前面的代替后面的 键值对 元素) Object .get(key); //依据键 返回对应的值对象不存在对应键 返回 null;Object .remove(key); //依据键 删除指定 键值对 对象int .size(); //返回元素个数boolean .containsKey( object key ); //判断 是否存在 指定键值对 对象;boolean .isEmpty(); //判断 是否存在 键值对 对象void .clear(); //清空该汇合所有 元素;Set .keySet(); //返回 键的汇合 Set类型汇合; set是Collection 的实现类, 通过 .iterator(); 办法返回一个Iterator对象,进行遍历!Collection .values(); //返回 值的汇合 collection 类型汇合;
实例:
@Testpublic void mapTest(){ Map map = new HashMap(); //只能存储 K 惟一, 值能够反复的元素,反复的key 会代替之前的数据! map.put(1, "AA"); map.put(1, "AA"); map.put(1, "BB"); map.put(2, "AA"); map.put(2, "BB"); map.put(3, "CC"); System.out.println("遍历所有的key-value"); System.out.println("形式一: entrySet()"); Set entrySet = map.entrySet(); //获取所有的: entrySet()办法返回一个实现Map.Entry接口的对象汇合 Iterator iterator1 = entrySet.iterator(); //set 属于 collection.iterator(); 获取迭代器! while (iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); } System.out.println(); System.out.println("形式二: 迭代器"); //形式二: Set keySet = map.keySet(); Iterator iterator2 = keySet.iterator(); while(iterator2.hasNext()){ Object key = iterator2.next(); Object value = map.get(key); System.out.println(key + "=====" + value); } System.out.println(); System.out.println("形式三: 通过ForEach循环进行遍历: 此数省略~"); System.out.println(); System.out.println("形式四: 通过Java8 Lambda表达式遍历:"); map.forEach((k, v) -> System.out.println("key: " + k + " value:" + v)); System.out.println(); System.out.println("依据Key 寻找value="+map.get(1)); System.out.println("remove(Object key) 依据key移除元素~"); System.out.println(); System.out.println("boolean containsKey(Object key): 是否蕴含指定的key"+map.containsKey(1)); System.out.println(); System.out.println("map.clear() 清空map,与map = null操作不同"); System.out.println("map.isEmpty() 判断map是否为空");}
Map.Entry的定义
Map的entrySet()办法返回一个实现Map.Entry接口的对象汇合
- 汇合中每个对象都是底层Map中一个特定的键/值对
通过这个汇合的迭代器
取得每一个条数据的键或值
Map.Entry中的罕用办法
- Object getKey(): 返回条目标关键字
- Object getValue(): 返回条目标值
- Object setValue(Object value): 将相干映像中的值改为value,并且返回旧值
常见面试题:
汇合源码解析
HashMap的底层实现原理?
在 JDK 1.7 中 HashMap 是以数组加链表的模式组成的
JDK 1.8 之后新增了红黑树的组成构造,当链表大于 8
并且容量大于 64
时,链表构造会转换成红黑树结构
HashMap 基于 Hash 算法实现的:
- 当咱们往Hashmap 中 put 元素时
首先计算
key
的hash
值,这里调用了hash
办法hash
办法理论是让key.hashCode()
与key.hashCode()>>>16
进行, 异或操作 目标是缩小碰撞>>> 无符号右移 3>>>1: 3无符号右移一位等于 3/2=1 (三 除 二的一次幂) 二进制码最高位无论是 0 或1 都0示意,后果为一个负数;& 与运算符 二进制码 0: true 1:false 将两个二级制码一一位 码进行比拟,返回成一个新的二级制码; 就是它的后果;^ 异运算符 二进制码 0: true 1:false 将两个二级制码一一位 码进行比拟,返回成一个新的二级制码; 就是它的后果;| 或运算符 二进制码 0: true 1:false 将两个二级制码一一位 码进行比拟,返回成一个新的二级制码; 就是它的后果;计算机的每个对象最终都会本义成二进制:0101010101001而: 与 异 或 就是针对这些二进制操作的运算符& 与运算符 a &= b; 等同于 a = a & b; 有0为0,否则为1 0 & 0 = 0; 1 & 1 = 1; 1 & 0 = 0; | 或运算符 有1为1,否则为0 0 | 0 = 0; 1 | 1 = 1; 1 | 0 = 1; ^ 异运算符 雷同为0,不同为1 0 ^ 0 = 0; 1 ^ 1 = 0; 1 ^ 0 = 1;异 或 与 就是将两边的对象的 二进制每一个值进行比拟,返回一个新的对象~
咱们都晓得HashMap 底层实现是: 数组+链表
JDK8: 数组+链表+红黑树
- ① 依据K 的 hashCode() 计算出
哈希值
进行取模算法, 失去在一个在数组上的坐标. - ② 判断数组的坐标上是否存在元素, 没有就
间接新增
, 如果存在则: ③ - ③ 与该坐标的元素 hash值一样, 则比拟两个元素的 equals(); 如果equals() 不同则新增, 如果雷同则不新增
笼罩该元素!
- JDK8.0
- ④ 遍历table[i],判断链表长度是否大于8, 大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;
- ⑤ 插入胜利后,判断理论存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容
HashMap 和 Hashtable的异同?
雷同:
- 都是 K-V 存储构造
- HashMap和HashTable都实现了Serializable接口,因而它反对序列化,实现了Cloneable接口,能被克隆
不同:
- HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75
- HashMap是非线程平安的,只是用于单线程环境下
多线程环境下能够采纳concurrent并发包下的concurrentHashMap
HashTable是线程平安的 - HashMap中key和value都容许为null
key为null的键值对永远都放在以table[0]为头结点的链表中
- HashTable在遇到null时,会抛出NullPointerException异样
HashMap中String、Integer这样的包装类适宜作为K?
String、Integer等包装类的个性可能保障Hash值的不可更改性和计算准确性,可能无效的缩小Hash碰撞的几率
- 都是final类型,即不可变性,保障key的不可更改性,不会存在获取hash值不同的状况
- 外部已重写了
equals()
、hashCode()
等办法,恪守了HashMap外部的标准
泛型
JDK5.0新增
- 是程序设计语言的一种格调或范式 ,
泛型容许程序员在强类型程序,编写代码时应用一些特定的[类型]
- 这种参数类型能够用在:
类
、接口
和办法中
,==别离被称为泛型类、泛型接口、泛型办法== 汇合中存储的对象 什么都是 Object 类型因而 每次获取 都须要 进行
类型判断转换
instanceof
泛型汇合 在汇合的根底上 指定存储类型
List<String>