集合
一种装数据的容器,类似于数组,但大小可变
主要用途:
1,提供不同数据结构:java提供了不同类型的集合,每一种集合类的数据结构不同导致其特点和使用场景也不同,开发人员可以通过具体的需求选择合适的数据结构来提高程序的效率和性能.
2,方便的数据操作:java集合提供了丰富的方法和功能,可以方便地进行数据的增删改查,排序,遍历等操作.开发人员可以利用这些方法和功能来简化代码,提高开发效率
1,collection(单列集合)
每个元素(数据)只包含一个值
Collection集合的特点:
List系列集合:元素有序,可重复,有索引
eg:ArrayList,LinkedList
Set系列集合:元素无序,不重复,无索引
eg:HashSet:无序,不重复,无索引
LinkedHashSet:有序,不重复,无索引
TreeSet:按照大小默认升序排列,不重复,无索引
Collection集合方法
所有单列集合均直接或间接继承于Collection
public boolean add(E e) //添加元素到集合中public void clear() //清空集合中所有元素public boolean remove(E e)//在集合中删除给定对象public boolean contains(Object obj) //判断集合中是否包含给定的对象public boolean isEmpty() //判断集合是否为空public int size() //返回集合是否为空public Object[] toArray() //把集合中的元素存储到数组中
Collection遍历及应用
1,使用Iterator迭代器:
优点:可以在遍历过程中修改集合的内容,比如删除元素
缺点:代码比较繁琐,需要手动调用hasNext()或者next()方法
2,使用foreach循环:
底层就是使用迭代器
优点:代码简洁,不需要手动调用迭代器的方法
缺点:不能在遍历过程中修改集合的内容
3,普通for循环遍历(针对于有索引的集合List):
优点:可以通过索引更改访问位置,更加灵活
缺点:代码较为繁琐,需要手动管理索引
4,使用Stream流和lambda
优点:代码简洁,提供丰富的操作方法,比如过滤,映射,规约等
缺点:不能在遍历过程中修改集合内容,一旦创建了Stream流就不能再次遍历
Iterator iterator():
返回集合中的迭代器对象,默认指向当前集合的第一个元素
迭代器中的常用方法:
boolean hasNext() //询问当前位置是否存在元素,存在返回true,不存在则返回false
E next() //获取当前位置的元素,并同时将迭代器对象指向下一个元素处Iterator<E> it = c.iterator();
while(it.hasNext()){String s = it.next();sout(s);
}
(增强for循环)for each循环:
for(int i:arr){sout(i);
}
2,双列集合
键值对(entry键值对对象)一一对应
一次添加一对数据,键不能重复,值可以重复
顶层接口:Map接口
1,put(K key,V value)//添加元素2,remove(O key)//根据键删除键值对元素3,clear()//移除所有的键值对元素4,B containsKey(O key)//判断集合是否包含指定键5,B containsValue(O v)//判断集合是否包含指定值6,B isEmpty()//判断集合是否为空7,I size()//返回集合长度
List集合
List集合是一种有序的集合,可以存储重复的元素,是单列集合框架的一员,继承于Collection接口
List集合特点:
1,元素有序:存储顺序和取出顺序一致
2,元素可重复:可以存储重复的元素
3,有索引:增加了和索引相关的增删改查方法
常用List实现类:
1,ArrayList:底层使用数组实现,支持快速随机访问,但增删元素较慢
2,LinkedList:底层使用双向链表实现,增删元素较快,但随机访问元素较慢
List集合特有方法
void add(int index,E element) //在指定位置插入指定的元素
E remove(int index)//删除指定索引处的元素,返回被删除的元素
E set(int index,E element)//修改指定索引处的元素,返回被修改的元素
E get(int index)//返回指定索引处的元素
ArrayList
最常用的一种集合泛型类
泛型<>:规定元素的数据类型
ArrayList底层操作机制源码分析
底层维护了一个Object类型的数组elementData
空参构造:在底层创建一个默认长度为0的数组(jdk7中为10)
如果是指定容量capacity的构造,则初始容量为capacity
添加元素时,会判断是否需要扩容
需要扩容:如果第一次添加先扩容到10,如果还需要扩容,就扩容到1.5倍
如果创建时使用的是指定容量构造,则扩容时直接扩容至1.5倍
增加数据:
list.add(E e);//将元素添加到列表末尾list.add(int index,E e)//将元素添加到指定索引list.get(int index)//获取对应索引的值list.size()//返回集合存储的元素个数list.remove(int index)//删除对应索引的元素,返回被删除的元素值list.remove()//删除元素,返回删除与否的布尔值list.set(int index,元素)//用指定元素替换对应索引的值,返回被修改的元素
使用场景:
需要频繁查询或者数据量不大时增删
在数据量大又需要增删时不建议使用
底层原理
创建对象时,将一个长度为0的Object类型数组赋值给elementData
添加元素时,会判断size是否等于数组的长度,如果相等,会调用grow()方法进行扩容,使用Arrays.copyOf方法将elementData传到新数组中,如果原数组长度为0,会传到一个长度为10的数组中.如果不为0,会传到一个长度等于size+size/2的数组中.
LinkedList
底层使用链表
常用方法
存在索引,用以帮助判断遍历时从前向后遍历还是从后向前遍历
Set集合
一种不允许包含重复元素的集合,继承于Collection接口
Set集合特点:
1,元素唯一:添加重复元素会失败
2,无序性:Set集合中的元素没有顺序,存取顺序不一定相同
但是每次打印的顺序是相同的
3,无索引:Set集合中没有带索引的方法
Set集合常用实现类如下:
1,HashSet:
基于哈希表实现,使用哈希算法存储元素
不保证元素的顺序,存取顺序可能不一致
允许存储null值
适用于一切对存储要求没有特殊要求且元素唯一的场景
2,LinkedHashSet:
继承于HashSet,底层使用哈希表和链表实现
保留元素的插入顺序,存取顺序一致
允许存储null值
适用于元素有序且唯一的场景
3,Tree:
基于红黑树实现,可以对元素进行排序
保证元素默认按照升序排列,或者按照指定的Comparator(比较器)进行排序
不允许存储null值
适用于需要对元素进行排序的场景
TreeSet集合应用
属于Set接口的实现类,底层使用红黑树数据结构来存储元素,TreeSet是有序的,不允许有重复元素
特点:
1,对于数值类型:Integer,Double默认按照数值大小进行升序排列
2,对于字符串类型,默认按照首字符的哈希值升序排列
3,对于无法直接排序的自定义对象,需要自己指定排序方式
排序方式:
1,自然排序:
自定义类实现Comparable接口,重写接口中compareTo方法,根据方法返回值进行指定元素位置
如果返回值为负数,表示存入的元素为较小值存在左侧
如果返回值为0,表示当前存入的元素跟集合中的元素重复了,不存
如果返回值为正数,表示存入的元素为较大值存在右侧
2,比较器排序:
TreeSet的带参构造方法使用的是"比较器排序"对元素进行排序的
比较器排序就是让TreeSet集合构造方法接收Comparator接口的实现类对象
重写Comparator接口中的compare(T o1, T o2)方法,指定排序规则
o1代表的是当前往集合中添加的元素,o2代表的是集合中已经存在的元素,排序原理和自然排序相同
两种排序方式都存在时默认使用比较器排序方式
可变参数概述
一种特殊的参数类型,用于表示方法的参数个数可以变化,在方法声明时,使用三个连续的省略号来表示可变参数,
在方法体中,可变参数会被当做数组进行处理,可以通过遍历或直接使用索引访问
可变参数只能作为方法的最后一个参数,一个方法只能有一个可变参数
eg:
public void printNumbers(int...numbers)
HashSet
HashSet原理及应用
底层创建一个hashmap对象,hashset即为一个只有键的hashmap
实现了Set接口,通过哈希表实现了高效的插入,删除和查找操作.可以快速判断一个元素是否存在集合中,并且不允许集合中存在重复的元素,但HashSet的迭代顺序并不确定,所以不保证元素的顺序是否和插入顺序一致
底层原理
数组+链表+红黑树
创建一个默认长度16的数组,默认负载因子为0.75,数组名字table
负载因子:元素个数>=长度*负载因子时会自动扩容
使用元素的哈希值对数组的长度求余计算出应该存入的位置
判断当前位置是否为null,如果为null直接存入
如果不为null比较不相等则存入
当链表长度超过8且数组长度>=64时,自动将链表转成红黑树
红黑树:
自平衡的二叉树
每个节点都有颜色属性(红/黑)
根节点一定为黑
红色节点不能相连
任何一个节点到叶节点经过的黑色节点数量相同
哈希值
一个十进制的整数,用来表示对象的特定标识.哈希值是通过哈希函数对对象的内部状态进行计算得到的
可以通过hashCode()方法来实现,该方法是object类的一个方法,所有java对象都可以调用
同一个对象多次调用hashCode()方法返回的哈希值是相同的,不同的对象,他们的哈希值一般不相同,但也有可能会相同(哈希冲突)
哈希冲突:因为哈希值是有限的,但对象数量是无限的,所以必然会有哈希冲突
使用规则
HashSet集合想要保证元素的唯一性,需要重写hashcode()和equals()方法
LinkedHashSet原理及应用
HashSet的一个子类,继承了HashSet的所有特性,保留了元素的插入顺序,可以理解为LinkedHashSet是一个有快速查找能力的链表,或是一个保留了插入顺序的哈希表
LinkedHashSet的特点:
1,插入顺序被保留
2,允许存储null元素
3,不允许重复元素,与hashset一样也会根据元素的哈希值判断是否为重复元素
集合遍历时,并发修改异常
当使用迭代器遍历时,如果对集合修改(增删),就会抛出ConcurrentModificationExcepiton(并发修改异常)
产生原因:
因为集合在遍历过程中使用了一个内部的标记来检测集合是否被修改,如果发现集合被修改,则会抛出异常,为了保证在多线程环境下,遍历集合的线程能正确地获取集合状态
解决方案;
使用迭代器内部的remove()方法来删除元素,remove()方法会更新内部的标记,避免并发修改异常
需要添加元素时:
可以使用ListIterator(迭代器的子接口列表迭代器)
可以使用其中的hasPrevious()方法倒着遍历
但需要先正向遍历到列表末尾
其中包含add()方法,可以添加元素,会在当前迭代器指向的元素之后添加元素
或者干脆直接使用for循环,一定不会发生并发修改异常
Collections工具类
提供了一系列静态方法用于对集合进行操作和处理,包含了一些操作集合的工具方法,这里我稍微挑几个我觉得有用的记录一下
static boolean addAll(Collection c,T ...elements)//将所有元素全部添加到集合内
static List emptyList() //返回空列表
static void reverse(List list)//反转集合
static void sort()//排序
static void swap(List list,int i,int j)//交换集合中的两个元素
Map集合介绍
双列集合
一种键值对数据结构,提供了一种映射关系,可以将键值进行关联,Map集合以键的唯一性来存储和操作元素(主键?),每个键对应一个值,
键唯一,但值可以相同
Map集合具有以下特点:
1,键的唯一性:map集合中的键是唯一的,不允许重复键存在,如果尝试放入重复键,会将旧值覆盖
2,无序性:map集合中的元素是无序的,即存取顺序和插入顺序无关
3,可以存储null值和null键,map集合可以存储null值和null键,但是需要注意,只能同时存在一个null键
MAP集合常见的使用场景:
1,存储和快速查找:map集合可以用来存储键值对,通过键快速查找对应值,适用于需要频繁查找和操作数据的场景
2,统计和计数:可以用来统计数据频率或者次数,eg将元素作为键,次数作为值
3,配置信息:Map集合可以用来存储配置信息,eg将属性名作为键,属性值作为值,以便于读取和修改配置信息
Map集合常用实现类
1,HashMap:HashMap是最常用的Map实现类,基于哈希表实现,能快速查找和插入操作,不保证元素顺序,允许存储null键和值,但不保证线程安全
无序性,允许空键和空值,高效性,保证键唯一
2,TreeMap:TreeMap是基于红黑树实现的有序Map集合,根据键的自然顺序或者自定义比较器对键进行排序,保证元素按照键顺序排列,不允许存储null键但可以存储null值
有序性,不允许空值,保证键唯一
3,LinkedHashMap:LinkedHashMap是HashMap的一个子类,在HashMap的基础上增加了链表维护键值对的插入顺序,保证了元素的插入顺序,允许存储null键和null值,但不保证线程安全
保持插入顺序:可以按照元素插入的顺序进行排序,也可以按照最近访问的顺序进行排序
允许空键和空值,键唯一
Map集合常用方法
public v put(K key,V value) //添加元素(也可以修改)
//如果put覆盖了元素就返回被覆盖元素的值
public int size() //获取集合的大小
public void clear()//清空集合
public boolean isEmpty()//判断集合是否,为空返回true
public v get(Object key)//根据键获取对应值
public v remove(Object key)//根据键删除整个元素
public boolean containsKey(Object key)//判断是否包含某个键
public boolean containsValue(Object value)//判断是否包含某个值
public Set<K> keySet() //获取全部键的集合
public Collection<V> values()//获取Map集合的全部值
Map的遍历:
方法1:
keySet():返回一个包含所有键的Set集合,可以通过遍历这个集合来获取每个键
for(String key:map.keySet()){Integer value = map.get(key);//再通过获取到的键进行操作
}
get(Object key)根据指定的键获取对应的值
方法2:
Entry为Map的接口的内部接口,要使用
要么使用Map.Entry
要么导包
entrySet():返回一个Set集合,包含Map中所有键值对的集合,每个键值对都是一个Map.Entry对象
getKey():获取当前Map.Entry对象的键
getValue():获取当前Map.Entry对象的值
Map<String,Integer> map = new HashMap<>();
map.put("A",1);
map.put("B",2);
map.put("C",3);
for(Map.Entry<String,Integer> entry : map.entrySet()){String key = entry.getKey();Integer value = entry.getValue();//得到Entry键值对,进行操作
}