集合、数组都是对多个数据进行存储操作(主要是内存层面存储)的结构,简称Java容器。
数组的特点
1.数组初始化以后,长度确定不可变
2.数组定义好,其元素的类型确定不可变(可能有多态性)
3.数组中提供的方法有限,对于添加、删除、插入数据等操作不方便。
4.获取数组中实际元素的个数是没有办法的。
5.数组存储数据的特点是有序、可重复的。
Java集合可分为Collection和Map两种体系,集合存储的优点是解决数组存储数据方面的弊端。
- Collection接口:单列数据,用来存储一个一个的对象
- List接口:元素有序,可重复的集合 --> '动态'数组
- Set接口 :元素无序、不可重复的集合
- Map接口:双列数据,保存有映射关系(键值对)的集合
Collection接口
向collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals方法。
Abstract Methods
add(Object e):
将元素e添加到集合中
size():
获取添加的元素个数
addAll(Collection coll):
将形参coll集合的元素添加到当前集合中
clear():
清空集合元素,集合仍然存在,只是集合里没有元素
isEmpty():
判断当前集合是否为空
contains(Object obj):
判断当前集合中是否包含obj,是否包含是通过调用obj的equals判断
containsAll(Collection coll):
判断形参coll中的所有元素是否都存在当前集合中。
remove(Object obj):
移除某个元素,同样通过equals寻找移除的元素
removeAll(Collection coll):
从当前集合中移除coll集合中所有的元素,需要调用equals函数
retainAll(Collection coll):
求两个集合的交集,结果为修改当前集合后的集合。
equals(Collection coll):
比较两个集合是否一样。
hashCode():
返回当前对象的哈希值
集合的遍历:iterator 接口
集合元素的遍历操作:Iterator迭代器接口。只能遍历集合Collection,map不行
Iterator <E> iterator = coll.iterator(); //方式一:通过iterator.next()获得集合元素 iterator.next(); //方式二:循环,不推荐 for(int i =0;i<coll.size;i++){ iterator.next(); } //方式三:推荐! //hasNext():判断是否还有下一个元素 while(iterator.hasNext()){ //next():①指针下移 ②将下移以后集合位置上的元素返回 Object obj = iterator.next(); if("Tom".equals(obj)){ //iterator中的remove(),删除集合中的元素 iterator.remove() } } //方法引用,Java8新特性 coll.forEach(System.out::println)
集合的遍历:增强for循环
增强for循环用于遍历数组和集合(colleciton)
格式:集合元素的类型 局部变量:集合
//第一个位置,是当前的元素 //第二个位置,是遍历的对象 //集合元素的类型 局部变量 :集合 for(Object obj : coll){}
面试题
知识点:增强for只能遍历不能赋值
集合元素的类型 局部变量:集合
相当于从集合中取出赋值给局部变量,集合本身是没有修改的。
String [] arr = new String[]{"ranan","ranan"} //普通for循环 for(int i=0;i<arr.length;i++){ arr[i] = "xx" } for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); //输出"xx" } //增强for循环 for(String s:arr){ //这里的s是局部变量,相当与取出arr[i]的值赋值给了变量s,改变的是s的值 s = "xx"; } for(int i=0;i<arr.length;i++){ System.out.println(arr[i]); //输出"ranan" }
List接口
比较ArrayList、LinkedList、Vector的异同
同:三各类都实现了List接口,存储数据的特点相同:存储有序的、可重复异:
- ArrayList:List接口的主要实现类,线程不安全、效率高,底层使用Object[]存储,默认扩容为原数组的1.5倍
- LinkedList:底层使用双向链表存储,对于频繁的插入、删除操作效率更高
- Vector:List接口的古老实现类,线程安全、效率低,底层使用Object[]存储,默认长度为10。默认扩容为原数组的2倍
ArrayList
JDK7
//底层创建了长度是10的object[]数组 ArrayList <Integer> list = new ArrayList<Integer>();//一般扩容原来的一半,将原数组元素复制到新数组 List add(123) //底层使用Object[]数组存储,elementData[0]= new Integer(123) ArrayList list = new ArrayList(数字)
结论:建议开发中使用带参的构造器
JDK8
ArrayList list = new ArrayList() //底层的object[] elementData初始化为{},并没有创建长度为10的数组
懒汉式,底层的object[] elementData初始化为{},并没有创建长度为10的数组,第一次调用add方法时才new创建好数组。后续的添加和扩容操作与jdk7中一样。
区别:JDK7中的ArrayList对象的创建类似于单例的饿汉式,而JDK8中的类似于单例的懒汉式,延迟了数组的创建,节省内存
LinkedList
LinkedList = new LinkedList()//内部声明了Node类型的first与last,值为null
removeLast():移除最后一个元素
List的常用方法
List集合添加了一些根据索引来操作集合元素的方法,因为List是有序的
void add(int index,Object ele):
在index位置插入ele元素
boolean addAll(int index,Collection eles):
从index位置开始依次插入eles中的所有元素
Object get(int index):
获取指定index位置的元素
int indexOf(Object obj):
返回obj在集合中首次出现的位置
int LastIndexOf(Object obj):
返回obj在集合中最后一次出现的位置
Object remove(int index):
移除指定index位置的元素,并返回此元素
Object set(int index,Object ele):
设置指定index位置的元素为ele
List subList(int fromIndex,int toIndex):
返回从fromIndex到toIndex位置的子集合
常用方法
增:add(Object obj)
删:remove(int index)/remove(Obejct obj)
改:set(int index,Object ele)
查:get(int index)
插:add(int index,Object ele)
长度:size()
遍历:Iterator
迭代器方法/增强for循环/普通的循环
集合与数组的转换
集合转换成数组 --> coll.toArray()
数组转换成List --> Arrays.asList(new String[]{'AA','BB','CC'})
使用场景:生成一个不可更改的list
Arrays.asList
1.该方法适用于对象型数据的数组(String、Integer
...)
2.该方法不建议使用于基本数据类型的数组(byte,short,int,long,float,double,boolean
)
3.该方法将数组与List列表链接起来:当更新其一个时,另一个自动更新
4.不支持add()、remove()、clear()等方法。用此方法得到的List的长度是不可改变的
List arr = Arrays.asList(new int[]{123,456})会把new int[]{123,456}整体看成一个元素,基本数据类型数组会被看成一个整体。可以写成 List arr = Arrays.asList(123,456)
Set接口
Set接口中没有额外定义新的方法。
比较HashSet、LinkedHashSet、TreeSet
同:都是Set接口的实现类,存放数据无序、不可重复
HashSet:Set接口的主要实现类,线程不安全,可以存放null值,数组+链表存储数据
LinkedHashSet:HashSet的子类,遍历其内部数据时可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet:数据是同一类的,可以按照添加对象的指定属性,进行排序。
无序性与不可重复性
无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的hash值决定。(数组中的存储不是按照添加顺序添加的)
不可重复性:先比较添加元素的hash值是否相同,相同按照equals()判断是否重复
HashSet中元素的添加过程
向HashSet中添加元素a
1.先调用元素a所在类的hashCode()方法,计算a的哈希值。
2.此hash值通过散列函数计算出在HashSet底层数组中的存放位置 + 拉链法解决冲突
散列函数 + 拉链法解决冲突 拉链法时要先比较hash值再按照equals()判断是否重复,不重复时再链上
要求:向Set中添加的数据,其所在类的一定要重写HashCode方法和equals方法,此两个方法尽可能保持一致性。(相等的对象必须具有相同的hash值)
注意:
new HashSet()实际上是new了一个HashMap,向HashSet添加元素实际上是向HashMap添加元素,在HashSet中添加的元素相当于是HashMap的key,value都指向一个对象(这个对象没什么意义)
LinkedHashSet
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用指针,遍历其内部数据时可以按照添加的顺序遍历
TreeSet
要求:向TreeSet中添加的数据,要求是相同类的对象作用:进行排序
遍历的时候会调用排序方法进行自动排序。
自然排序(Comparable接口):比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
定制排序(Comparator接口):比较两个对象是否相同的标准为:compare()返回0,不再是equals()
Comparator com = new Comparator(){ //定制排序内容 @override public int compare(Object o1,Object o2){} } TreeSet set = new TreeSet(com)
练习题
1.在List内去除重复数字值,要求尽量简单
HashSet常常用于过滤重复元素 --> set.addAll(list) --> return new ArrayList(set)
//Person类中重写了hashCode()和equals()方法 HashSet set = new HashSet(); Person p1 = new Person(1001,"AA"); Person p2 = new Person(1002,"BB"); set.add(p1); set.add(p2); p1.name = "CC"; set.remove(p1);//remove时先计算hash值(此题由id和name共同计算所得),内容变了hash值也变了 System.out.println(set);//1001,"CC" 1002,"BB" set.add(new Person(1001,"CC"));//原来的p1的hash值是按照1001,"AA"计算所得,此时算出来的应该是新的hash值 System.out.println(set);//1001,"CC" 1002,"BB" 1001,"CC" set.add(new Person(1001,"AA")); //这里算出来是p1的hash值,hash值相同调用equals方法发现不同 System.out.println(set);//1001,"CC" 1002,"BB" 1001,"CC" 1001,"AA"
Map接口
Map实现类的结构
HashMap:
作为Map的主要实现类,线程不安全,效率高。可以存储key/value = null
LinkedHashMap:
在遍历map元素时,可以按照添加的顺序实现遍历。原因:在原有的HashMap底层结构基础上添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,LinkedHashMap效率高于HashMap
TreeMap:
保证按照添加的key-value对进行排序(key),实现排序遍历,底层使用红黑树
Hashtable
:作为古老实现类,线程安全,效率低,不可以存储key/value = null
Properties:
常用来处理配置文件,key-value都是String类型
Entry(key,value)
无序,不可重复(Set)
key无序,不可重复,用set存储。 --> 不可重复,key所在的类要重写quals()
和hashCode()
(HashMap为例)
value无序,可重复 --> value所在的类要重写equals,重写是为了实现通过value找到key的方法
Map中的常用方法
增删改
Object put(Object ket,Object value):
将指定key-value添加到(或修改key的值)当前map对象
void putAll(Map m):
将m中的所有key-value对存放在当前map中
Object remove(Object key):
移除指定key的key-value对,并返回value
void clear():
清空当前的map中的数据,map还在,只是里面没数据了。map.size()不会报错
查
Object get(Object key):
获取指定key对用的value
boolean containsKey(Object key):
是否包含指定的key
boolean containsValue(Object key):
是否包含指定的key
int size():
返回map中键值对的个数
boolean isEmpty():
判断当前map是否为空
boolean equals(Object obj):
判断当前map和参数对象obj是否相等
遍历
Set keySet():
返回所有key构成的Set集合
Collection values():
返回所有value构成的Collection集合
Set entrySet():返回所有key-value对构成的Set集合,Set<Map.Entry<Character,Integer>> entry = map.entetSet()
//返回的是Collection之后就可以使用iterator迭代器 Set set = map.keySet(); Iterator iterator = set.iterator(); //遍历所有的key-value Set entrySet = map.entrySet() Iterator iterator = entrySet.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey()+"--->"+entry.getValue()); } //写法二 Set <Map.Entry<String,Integer>> entrySet = map.entrySet(); Iterator <Map.Entry<String,Integer>> iterator = entrySet.iterator(); while(iterator.hasNext()){ Map.Entry <String,Integer> entry = iterator.next(); String key = entry.getKey(); Integer value = entry.getValue(); }
HashMap
问题1:HashMap的底层实现原理?
JDK7:
//在实例化以后,底层创建了长度16的一维数组Entry[] table HashMap<E,E> map = new HashMap<E,E>(); //...可能已经执行过多次put... map.put(key1,value);
往map中添加键值对的过程
- 调用key1所在类的hashCode()计算key1哈希值,得到在Entry中的存放位置
- 检查该位置是否有数据
- 没有数据,此时key1-value1添加成功
- 有数据,比较key1和已存在的数据的哈希值
- 如果哈希值一样,调用equals()方法,不相等则添加成功,相等用新的value替换旧的value。put有更新作用
- 如果哈希值不一样,则添加成功
在不断的添加过程中,默认的扩容方式:扩容为原有容量的2倍,并将原有的数据复制。
JDK8相较于JDK7的不同点(类似ArrayList)
1.new HashMap():底层没有创建一个长度为16的数组
2.首次调用put()方法时,底层创建长度为16的数组
3.jdk8 底层的数组是:Node[]
4.链表插入的方法(七上八下)
5.jdk7底层结构:数组+链表。jdk8中底层结构:数组+链表+红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64(若>8但是<64则扩容),此时该索引位置上的所有数据使用红黑树存储,
问题2:负载(加载)因子值的大小,对HashMap有什么影响?
HashMap的默认容量16
HashMao的默认加载因子0.75
threshold扩容的临界值=容量*填充因子=16 x 0.75=15
负载因子的大小决定了HashMap的数据密度
负载因子越大密度越大,发生碰撞的几率越高,性能会下降
负载因子越小,越容易触发扩容,会浪费空间
LinkedHashMap
LinkedHashMap是HashMap的子类,底层还是按照HashMap去存。
类似LinkedHashSet,在遍历map元素时,可以按照添加的顺序实现遍历。
LinkedHashMap中的内部类多了befor和after指针
TreeMap
要求:向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
排序:按照key进行排序
自然排序(Comparable接口):
比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
定制排序(Comparator接口):
比较两个对象是否相同的标准为:compare()返回0,不再是equals()
//自然 TreeMap map = new TreeMap(); //添加的key类里重写compareTo() //定制 Comparator com = new Comparator(){ //定制排序内容 @override public int compare(Object o1,Object o2){} } TreeMap map = new TreeMap(com)
Properties
c类是Hashtable的子类,该对象用于处理属性文件。
由于属性文件里的key-value都是字符串类型,所以Properties里的key和value都是字符串类型。
存储数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
需要在当前项目中添加配置文件,File(手动添加后缀.properties)/Resource Bundle(自动添加后缀)
Properties pros = new Properties(); FileInputStream fis = new FileInputStream("配置文件的名字") //参数为流,加载流对用的文件 pros.load(fis); fis.close();
Collections工具类
Collections是一个操作Set、List和Map等集合的工具类。
Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变,对集合对象实现同步控制等方法
排序方法
reverse(List):
反转List中元素的顺序
shuffle(List):
对List集合元素进行随机排序
sort(List):
根据元素的自然顺序对指定list集合元素按升序排序
sort(List,Comparator):
根据指定的Comparator产生的顺序对List集合元素进行排序
swap(List,int,int):
将指定list集合中的i处元素和j处元素进行交换
Object max/min(Collection):
根据元素的自然排序,返回给定集合中的最大/最小元素
Object max/min(Collection,Comparator):
根据Comparator指定的顺序,返回集合中的最大/最小元素
int frequency(Collection,Object):
返回指定集合中指定元素出现的次数
void copy(List dest,List src):
将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Objec newVal
):把List中所有的旧值改成新值
copy的注意事项
//************错误写法1 //错误原因:需要满足src.size()<= dest.size(),否则会报错 List dest = new ArrayList(); //...一堆list.add() List dest = new ArrayList(); Collections.copy(dest,list); //************错误写法2 //错误原因:传参是指定造底层数组的长度,需要的dest.size是填了几个 List dest = new ArrayList(); //...一堆list.add() List dest = new ArrayList(list.size()); Collections.copy(dest,list); //************正确写法 List dest = new ArrayList(); //...一堆list.add() //asList数组转化成List,这里的数组会默认填充null List dest = Arrays.asList(new Object[list.size()]) Collections.copy(dest,list);
Collections常用方法:同步控制
Collections类中提供了多个synchronizedXxx(xxx)方法,将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。
ArrayList和HashMap都是线程不安全的,如果要求线程安全可以使用synchronizedList(List list)和synchronizedMap(Map map)
//返回的List1为线程安全的List List list1 = Collections.synchronizedLisd(list);
总结
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注自学编程网的更多内容!
- 本文固定链接: https://zxbcw.cn/post/215931/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)