概述
集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用。为了满足不同的业务场景需求Java提供了很多不同特点的集合给我们选择。
常见集合 | ||
Collection | Set | Map |
List | TreeSet | HashMap |
ArrayList | HashSet | TreeMap |
LinkedList | LinkedHashSet | LinedHashMap |
集合这么多主要有以Collection为代表的单列集合和以Map为代表的双列集合。单列集合中每个元素(数据)只包含一个值,双列集合中每个元素包含两个值(键值对)。
Collection
Collection是一个种泛型接口,下边还有很多很多子接口,常用的有List和Set这两个。再下边还分很多实现类。
Collection集合特点
●List系列集合:添加的元素是有序、可重复、有索引。
◆ArrayList:有序、可重复、有索引。
◆LinekdList:有序、可重复、有索引
ArrayList与LinkedList底层采用的数据结构(存储、组织数据的方式)不同,应用场景不同。
●Set系列集合:添加的元素是无序、不重复、无索引。
◆HashSet:无序、不重复、无索引。
◆LinkedHashSet:有序、不重复、无索引。
◆TreeSet:按照大小默认升序排序、不重复、无索引。
Collection的常用方法
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
方法 | 说明 |
public boolean add(E e) | 添加元素,添加成功返回true. |
public void clear() | 清空集合的元素。 |
public boolean isEmpty() | 判断集合是否为空是空返回true,反之。 |
public int size() | 获取集合的大小。 |
public boolean contains(0bject obj) | 判断集合中是否包含某个元素。 |
public boolean remove(E e) | 删除某个元素;如果有多个重复元素默认删除前面的第一个! |
public 0bject[] toArray() | 把集合转换成数组 |
a.add(e) | 将集合e全部添加到a中 |
把集合转换成数组时还可以转换成一个具体类型的数组(如下)
Collection<String> c = new ArrayList<>();
c.add("xxx");
String[] arr2 = c.toArray(new String[c.size()]);
Collection的遍历
Collection集合可能没有索引可能不全都可以用for循环遍历,但都可以用迭代器、增强for循环、lambda表达式遍历。
迭代器
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
Collection集合获取迭代器的方法
方法名称 | 说明 |
Iterator<E> iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素 |
Iterator迭代器中的常用方法
方法名称 | 说明 |
boolean hasNext( ) | 询问当前位置是否有元素存在,存在返回true ,不存在返回false |
E next() | 获取当前位置的元素,并同时将迭代器对象指向下一个元素处。 |
Collection<String> c = new ArrayList<>();
c.add("First");
c.add("Second");
c.add("Third");
Iterator<String> it = c.iterator();
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
//结果是:
//First
//Second
//Third
System.out.println(it.next());//这里会出现异常,因为已经没有数据可以取了。
//下列代码可以直接取出集合中的全部数据。
while (it . hasNext()){
String ele = it.next();
System. out. printLn(ele);
}
增强for循环
增强for可以用来遍历集合或者数组。增强for遍历集合,本质就是迭代器遍历集合的简化写法。
格式
for(数据类型变量名 : 被遍历的集合(collection)或者数组) {
执行语句
}
Collection<String> c = new ArrayList<>();
c.add("First");
c.add("Second");
c.add("Third");
for(String ele: c){
System.out.println(ele);
}
lambda表达式
●得益 于JDK 8开始的新技术Lambda表达式,提供了-种更简单、更直接的方式来遍历集合。
需要使用Collection的如下方法来完成
方法名称 | 说明 |
default void forEach(Consumer<? super T> action) | 结合lambda遍历集合 |
Collection<String> c = new ArrayList<>();
c.add("First");
c.add("Second");
c.add("Third");
list. forEach(s -> {
System. out. println(s);
});
//用匿名内部类写
c.forEach(new Consumer<String>() {
@0verride
public void accept(String s) {
System.out.println(s);
}
});
//用方法引用进一步简写
c.forEach(System.out::printLn);
List集合
List集合的特有方法
List集合因为支持索引, 所以多了很多与索引相关的方法,当然,,Collection的功能List也都继承了。
方法名称 | 说明 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引|处的元素 |
遍历方式
List的遍历方式:1.for循环(因为List集合有索引)2.迭代器 3.增强for循环 4.Lambda表达式
ArrayList集合
ArrayList集合的底层原理
- 基于数组实现。
- 查询速度快(根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
- 删除效率低(可能需要把后面很多的数据进行前移。)
- 添加效率极低(可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。)
- 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组。
- ②添加第一个元素时,底层会创建一个新的长度为10的数组。
- 存满时,会扩容1.5倍。
- ④如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准。
ArrayList集合适合的应用场景
- ArrayList适合;根据索引查询数据。比如根据随机索引取数据 (高效)!或者数据量不是很大时!
- ArrayList不适合数据量大的同时,又要频繁的进行增删操作!
LinkedList集合
链表
- 链表中的结点是独立的对象, 在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
- 查询慢,无论查询哪个数据都要从头(尾)开始找。
- 链表增删相对快。(对首尾元素进行增删改查的速度是极快的)
LinkedList集合的底层原理
- 基于双链表实现的。
- 特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
LinkedList新增方法
LinkedList新增了:很多首尾操作的特有方法。
方法名称 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst( ) | 返回此列表中的第一个元素 |
public E getLast( ) | 返回此列表中的最后一个元素 |
public E removeFirst( ) | 从此列表中删除并返回第一个元素 |
public E removeLast( ) | 从此列表中删除并返回最后一个元素 |
LinkedList的应用场景
- 可以用来设计队列(队列的特点:先进先出,后进后出)
- 可以用来设计栈(栈的特点:后进先出,先进后出)数据进入栈模型的过程称为:压/进栈(push),数据离开栈模型的过程称为:弹/出栈(pop)。(可以用LinkedList的方法push、pop)
Set 集合
简述
Set要用到的常用方法,基本上就是Collection提供的! !
自己几乎没有额外新增一些常用功能!
HashSet
哈希值
- 哈希值就是一个int类型的数值,Java中每个对象都有一个哈希值。
- Java中的所有对象,都可以调用0bejct类提供的hashCode方法,返回该对象自己的哈希值。
public int hashCode( ):返回对象的哈希码值。
对象哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
- 不同的对象,它们的哈希值一般不相同, 但也有可能会相同(哈希碰撞)。
哈希表
哈希表是一种增删改查数据性能都较好的结构。
- JDK8之前,哈希表=数组+链表
- JDK8开始,哈希表=数组+链表+红黑树
HashSet集合的底层原理
●基于哈希表实现。
●哈希表是一种增删改查数据,性能都较好的数据结构。
- 创建一个默认长度16的数组,默认加载因子为0.75,数组名table
- 使用元素的哈希值对数组的长度求余计算出应存入的位置
- 判断当前位置是否为null,如果是nul直接存入
- 如果不为null,表示有元素,则调用equals方法比较相等,则不存;不相等,则存入数组
●jDK8之前,新元素存入数组,占老元素位置,老元素挂下面
●jDK 8开始之后,新元素直接挂在老元素下面。当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树,进一步提高操作数据的性能。
HashSet集合去重复的机制
HashSet集合默认不能对内容一样的两个不同对象去重复!
如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法
LinkedHashSet底层原理
- 依然是基于哈希表(数组、链表、红黑树)实现的。
- 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。
TreeSet
概述
- 特点: 不重复、无索引、可排序(默认升序排序,按照元素的大小,由小到大排序)
- 底层是基于红黑树实现的排序。
注意:
- 对于数值 类型: Integer , Double,默认按照数值本身的大小进行升序排序。
- 对于字符串类型: 默认按照首字符的编号升序排序。
- 对于自定义类型如Student对象,TreeSet默认是无法直接排序的。(会报错)
自定义排序规则
TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。
方式一
让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
方式二
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则。
两种方式中,关于返回值的规则:
- 如果认为第一个元素 > 第二个元素返回正整数即可。
- 如果认为第一个元素 < 第二个元素返回负整数即可。
- 如果认为第一个元素 = 第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
注意:如果类本身有实现Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。
Collection集合小结
1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据。
●用ArrayList集合 (有序、可重复、有索引),底层基于数组的。(常用)
2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多。
●用LinkedList集合(有序、可重复、有索引) ,底层基于双链表实现的。
3.如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快。
●用HashSet集合 (无序,不重复,无索引),底层基于哈希表实现的。(常用)
4.如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快。
用LinkedHashSet集合(有序,不重复,无索引),底层基于哈希表和双链表。
5.如果要对元素进行排序,也没有重复元素需要存储,且希望增删改查都快。
●用TreeSet集合, 基于红黑树实现。
集合的并发修改异常问题
- 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
- 由于增强for循环遍 历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误。
增强for循环、lambda表达式遍历也会并发修改异常问题且无法解决。
List<String> List = new ArrayList<>();
list.add("AC");
list.add("SD");
list.add("AD");
list.add("AJ");
list.add("CSD");
Iterator<String> it = list.iterator();
while (it.hasNext()){
String name = it.next() ;
if (name.contains("A")){
// list.remove (name); //并发修改异常的错误。
it.remove(); //删除迭代器当前遍历到的数据,每删除一个 数据后,
}
}
保证遍历集合同时删除数据时不出bug的方法
- 使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可。
- 如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。
Collection的其他相关知识
可变参数
- 可变参数就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型...参数名称;
- 特点:可以不传数据给它;可以传一或者同时传多个数据给它,也可以传一个数组给它。
- 好处:常常用来灵活的接收数据。
public cLass ParamTest {
public static void test(int... nums){
//可变参数在方法内部,本质就是个数组。
}
public static void main(String[] args) {
test(); //不传数据
test(10); //传输一个数据给它
test(10,20,30); //传输多个数据给它
test(new int[]{10, 20, 30,40}); //传输一个数组给可变参数
}
}
注意事项
- 一个形参列表中,只能有一个可变参数。
- 可变参数必须放在形参列表的最后面
Collections
概述
Collections是一个用来操作集合的工具类
Collections提供的常用静态方法
方法名称 | 说明 |
public static <T> boolean addAll(Collection<? super T> C, T... elements) | 给集合批量添加元素 |
public static void shuffle(List<?> list) | 打乱List集合中的元素顺序 |
public static <T> void sort(List<T> list) | 对List集合中的元素进行升序排序(本方法可以直接对自定义类型的List集合排序,但自定义类型必须实现了Comparable接口,指定了比较规则才可以。) |
public static <T> void sort(List<T> list, Comparator<? super T> c) | 对List集合中元素,按照比较器对象指定的规则进行排序 |
Collections只能支持对List集合进行排序
Map
概述
- Map集合称为双列集合, 格式: {key1=value1 , key2=value2 , key3=value3...},一次需要存一对数据做为一个元素.
- Map集合的每 个元素"key=value" 称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“ 键值对集合”。
- Map集合的所有 键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值
Map集合的特点
注意: Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的
●HashMap ( 由键决定特点) : 无序、不重复、无索引; (用的最多)
●LinkedHashMap ( 由键决定特点) :由键决定的特点:有序、不重复、无索引。
●TreeMap ( 由键决定特点) :按照大小默认升序排序、不重复、无索引。
常用方法
●Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。
常用方法 | 说明 |
public void put( k ,v) | 添加元素:无序,不重复,无索引。 |
public int size() | 获取集合的大小 |
public void clear() | 清空集合 |
public boolean isEmpty() | 判断集合是否为空, 为空返回true , 反之! |
public V get(0bject key) | 根据键获取对应值 |
public V remove(0bject key) | 根据键删除整个元素(删除键会返回键的值) |
public boolean containsKey(0bject key) | 判断是否包含某个键,包含返回true ,反之! |
public boolean containsValue(0bject value) | 判断是否包含某个值。 |
public Set<K> keySet() | 获取Map 集合的全部键。 |
public Collection<V> values() | 获取Map 集合的全部值。 |
map1. putALl(map2) | 把map2集合中的元素全部倒入一份到map1集合中去。 |
遍历方式
键找值
先用keySet获取所有键的集合,再用get根据键获取其对应的值。
Map<String,Double> map = new HashMap<>();
map.put("A", 1.0);
map.put("B", 2.0);
map.put("C", 3.0);
Set<String> keys = map.keySet() ;
for (String key : keys) {
double value = map.get(key);
System.out.println(key + "是第" + value + "个");
}
键值对
把“键值对“看成一个整体进行遍历。(难度较大)
Map提供的方法 | 说明 |
Set<Map.Entry<K, V> > entrySet() | 获取所有”键值对”的集合 |
Map.Entry提供的方法 | 说明 |
K getKey() | 获取键 |
V getValue() | 获取值 |
Map<String, Double> map = new HashMap<>();
map.put("A", 1.0);
map.put("B", 2.0);
map.put("C", 3.0);
Set<Map.Entry<String, Double>> entries = map.entrySet();
for (Map.Entry<String, Double> entry : entries) {
String key = entry.getKey();
double value = entry.getValue();
System.out.println(key + "是第" + value + "个");
}
Lambda
JDK 1.8开始之后的新技术(非常的简单)。
需要用到Map的如下方法
方法名称 | 说明 |
default void forEach(BiConsumer<? super K,? super V> action) | 结合lambda遍历Map集合 |
Map<String, Double> map = new HashMap<>();
map.put("A", 1.0);
map.put("B", 2.0);
map.put("C", 3.0);
map. forEach((key,value ) -> {
System. out. printLn(key + "是第" + value + "个");
});
HasMap
●HashMap跟HashSet的底层原理是一 模一样的,都是基于哈希表实现的。
实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
LinkedHashMap
●底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。
实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。
TreeMap
●特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
●原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
TreeMap集合同样也支持两种方式来指定排序规则。
●让类实现Comparable接口,重写比较规则。
●TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。