集合

概述

集合主要分为单列集合、双列集合

Collection接口有两个重要的子接口List、Set,他们实现子类都是单列集合

Map接口的实现子类是双列集合,K-V

  • List:是一个有序集合,可以放重复的数据,有索引

    • ​ ArrayList、LinkedList:有序、可重复、有索引
  • Set:是一个无序集合,不允许放重复的数据,无索引

    • HashSet:无序、不重复、无索引
    • LinkedHashSet:有序、不重复、无索引
    • TreeSet:按照大小默认升序排序、不重复、无索引
  • Map:是一个无序集合,集合中包含一个键对象,一个值对象,键对象不允许重复,值对象可以重复(身份证号—姓名)

数组与集合

数组:

  • 长度开始时必须指定,而且一旦指定,不能改变
  • 保存的必须为同一类型的元素
  • 使用数组进行增加元素的代码麻烦

集合:

  • 可以动态保存任意多个对象,使用比较方便
  • 提供了一系列方便的操作对象的方法:add,remove,set,get
  • 使用集合添加、删除新元素,代码简洁
  • 集合和泛型都只支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都是对象

Collection集合

  1. collection实现子类可以存放多个元素,每个元素可以是Object
  2. 有些Collection的实现类,可以存放重复的元素,有些不可以
  3. Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的

Collection的常用方法

返回值 方法 含义
boolean add 添加单个元素
boolean addAll 添加多个元素
viod clear 清空
boolean contains 查找元素是否存在
boolean containsAll 查找多个元素是否都存在
boolean equals 比较此 collection 与指定对象是否相等。
boolean isEmpty 判断是否为空
boolean remove 删除指定元素
boolean removeAll 删除多个元素

Collection 接口遍历元素方式 1-使用 Iterator(迭代器)

Iterator对象称为迭代器,主要用于编列Collection集合中的元素

1
2
3
4
5
6
7
//得到当前集合的迭代器对象
Iterator<Object> iter = listname.iterator();
while(iter.hasNext){
Object outname = iter.next();
System.out.println(name);
}

  1. 迭代器的默认位置:索引0
  2. 越界会出现NoSuchElementException异常

Collection 接口遍历对象方式 2-for 循环增强

  • 内部原理是一个Iterator迭代器,遍历集合相当于迭代器的简化写法
  • 增强for循环:既可以遍历集合也可以遍历数组
  • 实现Iterable接口的类才可以使用迭代器和增强for,Collection接口已经实现了Iterable接口
1
2
3
4
Collection<String> list = new ArrayList<>();
for(String ele : list) {
System.out.println(ele);
}
  • 增强for不会改变原集合或数组中的值,修改无意义

Lambda表达式遍历集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//forEach常规写法
Collection<String> lists = new ArrayList<>();
lists.forEach(new Consumer<String>(){
@Override
public void accept(String s){
System.out.println(s);
}
});
//使用Lambda简化
lists.forEach(s -> {
System.out.println(s);
});
//简化:lists.forEach(s -> System.out.println(s));
//最简:lists.forEach(System,out::println);

List集合

List 接口下面主要有两个实现 ArrayList 和 LinkedList,他们都是有顺序的,也就是放进去

是什么顺序,取出来还是什么顺序,也就是基于线性存储,可以看作是一个可变数组

  • ArrayList:查询数据比较快,添加和删除数据比较慢(基于可变数组)
  • LinkedList:查询数据比较慢,添加和删除数据比较快(基于链表数据结构)
  • Vector:Vector 已经不建议使用,Vector 中的方法都是同步的,效率慢,已经被 ArrayList取代
  • Stack 是继承 Vector 实现了一个栈,栈结构是后进先出,目前已经被 LinkedList 取代

开发中,一般情况下,使用多态创建集合

List<Integer> l1 = new ArrayList<>();

初始化集合尽量指定初始化容量,默认指定为16

List的四种种遍历方式[ArrayList、LinkedList、Vector]

方式一:使用Iterator

1
2
3
4
Iterator iter = col.iterator();
while(iter.hasNext()){
Object o = iter.next();
}

方式二:使用增强for

1
2
3
for(Object o : col){

}

方式三:使用普通for

1
2
3
4
for(int i = 0;i<list.size();i++){
Object object = list.get(i);
System.out.print(object);
}

方式四:Lambda表达式

1
2
3
lists.forEach(s -> {
System.out.println(s);
});

遍历存在的问题

  • 迭代器遍历集合且直接用集合删除元素可能出现
  • 增强for遍历集合且直接用集合删除元素可能出现

解决方案

  • 用迭代器自身的remove去删除元素
  • 使用普通for循环采取i– 或倒序删除
  • 不能使用增强for循环删除集合中元素

ArrayList

  1. ArrayList是由数组来实现数据存储的
  2. ArrayList基本等同于Vector,除了ArrayList是线程不安全的(执行效率高),在多线程情况下,不建议使用ArrayList
  3. 可以加入多个null
方法 描述
add() 将元素插入到指定位置的 arraylist 中
addAll() 添加集合中的所有元素到 arraylist 中
clear() 删除 arraylist 中的所有元素
clone() 复制一份 arraylist
contains() 判断元素是否在 arraylist
get() 通过索引值获取 arraylist 中的元素
indexOf() 返回 arraylist 中元素的索引值
removeAll() 删除存在于指定集合中的 arraylist 里的所有元素
remove() 删除 arraylist 里的单个元素
size() 返回 arraylist 里元素数量
isEmpty() 判断 arraylist 是否为空
subList() 截取部分 arraylist 的元素
set() 替换 arraylist 中指定索引的元素
sort() 对 arraylist 元素进行排序
toArray() 将 arraylist 转换为数组
toString() 将 arraylist 转换为字符串
ensureCapacity() 设置指定容量大小的 arraylist
lastIndexOf() 返回指定元素在 arraylist 中最后一次出现的位置
retainAll() 保留 arraylist 中在指定集合中也存在的那些元素
containsAll() 查看 arraylist 是否包含指定集合中的所有元素
trimToSize() 将 arraylist 中的容量调整为数组中的元素个数
removeRange() 删除 arraylist 中指定索引之间存在的元素
replaceAll() 将给定的操作内容替换掉数组中每一个元素
removeIf() 删除所有满足特定条件的 arraylist 元素
forEach() 遍历 arraylist 中每一个元素并执行特定操作

ArrayList集合底层原理

  • ArrayList底层是基于数组实现的:根据索引定位元素块,增删需要做元素的移位操作
  • 当创建ArrayList对象时,如果使用的时无参构造器,则初始容量为0;第一次添加,则扩容至10,如需要再次扩容,则扩容1.5倍
  • 如果使用的时指定大小的构造器,则初始为指定的大小,如需扩容,直接扩容当前的1.5倍

ArrayList查询快,增删慢

Vector

  • Vector底层也是一个对象数组
  • Vector是线程同步的,即线程安全。Vector类的操作方法带有synchronized
  • 在开发中,需要线程同步安全,考虑Vector

ArrayList与Vector比较

ArrayList:可变数组;不安全,效率高;如果有参构造1.5倍;如果无参:第一次10、第二次1.5倍扩

Vector:可变数组;安全效率不高;如果无参,默认是10;之后2倍扩容

LinkedList

  1. LinkedList实现了双向链表和双端队列特点
  2. 可以添加任意元素(元素可重复),包括null
  3. 线程不安全,没有实现同步

LinkedList特有功能

方法名称 说明
addFirst(E e) 在该列开头添加指定元素
addLast(E e) 将指定的元素追加到此列表的末尾
getFirst() 返回此列表中的第一个元素
getLast() 返回此列表中的最后一个元素
removeFirst() 删除并返回第一个元素
removeLast() 删除并返回最后一个元素

LinkedList底层原理

  • 底层数据结构是双链表、查询慢、首尾操作的速度极快
  • LinkedList中维护两个属性first和last分别指向首节点和尾节点

ArrayList和LinkedList

底层结构 增删的效率 改查的效率
ArrayList 可变数组 较低、数组扩容 较高
LinkedList 双向链表 较高、链表追加 较低

Set接口

  1. 无序(添加与取出的顺序不一致,没有索引
  2. 不允许添加重复元素,最多只有一个null

注:

  • 不能使用索引来获取对象
  • 虽然是随机添加顺序,但取出的是固定的顺序

HashSet

  1. HashSet实现了Set接口
  2. HashSet实际上是HashMap
  3. 可以存放null值,但是只能有一个null
  4. HashSet不保证元素是有序的
  5. 不能有重复元素

HashSet底层机制

HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)

LinkedHashSet

  • LinkedHashSet是HashSet的子类
  • LinkedHashSet底层是LinkedHashMap,底层维护了一个数组+双向链表
  • LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
  • LinkedHashSet不允许添加重复元素

LinkedHashSet底层机制

  • LinkedHashSet维护了一个hash表和双向链表

Map接口*

  1. Map和Collection并列存在。
  2. Map中的key和value可以是任何引用类型的数据
  3. Map中的key不可以重复;value可以重复
  4. Map中的key和value可以为null;但key中只能有一个null
  5. 常用String类作为Map中的key
  6. key与value是一对一的关系

常用方法

方法 描述
clear() 删除 hashMap 中的所有键/值对
clone() 复制一份 hashMap
isEmpty() 判断 hashMap 是否为空
size() 计算 hashMap 中键/值对的数量
put() 将键/值对添加到 hashMap 中
putAll() 将所有键/值对添加到 hashMap 中
putIfAbsent() 如果 hashMap 中不存在指定的键,则将指定的键/值对插入到 hashMap 中。
remove() 删除 hashMap 中指定键 key 的映射关系
containsKey() 检查 hashMap 中是否存在指定的 key 对应的映射关系。
containsValue() 检查 hashMap 中是否存在指定的 value 对应的映射关系。
replace() 替换 hashMap 中是指定的 key 对应的 value。
replaceAll() 将 hashMap 中的所有映射关系替换成给定的函数所执行的结果。
get() 获取指定 key 对应对 value
getOrDefault() 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值
forEach() 对 hashMap 中的每个映射执行指定的操作。
entrySet() 返回 hashMap 中所有映射项的集合集合视图。
keySet() 返回 hashMap 中所有 key 组成的集合视图。
values() 返回 hashMap 中存在的所有 value 值。
merge() 添加键值对到 hashMap 中
compute() 对 hashMap 中指定 key 的值进行重新计算
computeIfAbsent() 对 hashMap 中指定 key 的值进行重新计算,如果不存在这个 key,则添加到 hasMap 中
computeIfPresent() 对 hashMap 中指定 key 的值进行重新计算,前提是该 key 存在于 hashMap 中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MapMethod {
public static void main(String[] args) {
//演示map接口常用方法
Map map = new HashMap();
map.put("邓超", new Book("", 100));//OK
map.put("邓超", "孙俪");//替换-> 一会分析源码
map.put("王宝强", "马蓉");//OK
map.put("宋喆", "马蓉");//OK
map.put("刘令博", null);//OK
map.put(null, "刘亦菲");//OK
map.put("鹿晗", "关晓彤");//OK
map.put("hsp", "hsp的老婆");
System.out.println("map=" + map);

// remove:根据键删除映射关系
map.remove(null);
System.out.println("map=" + map);
// get:根据键获取值
Object val = map.get("鹿晗");
System.out.println("val=" + val);
// size:获取元素个数
System.out.println("k-v=" + map.size());
// isEmpty:判断个数是否为0
System.out.println(map.isEmpty());//F
// clear:清除k-v
//map.clear();
System.out.println("map=" + map);
// containsKey:查找键是否存在
System.out.println("结果=" + map.containsKey("hsp"));//T
}
}
class Book {
private String name;
private int num;
public Book(String name, int num) {
this.name = name;
this.num = num;
}
}

HashMap小结

  1. Map接口的常用实现类:HashMap、Hashtable、Properties
  2. HashMap是Map接口中使用频率最高的实现类
  3. HasMap是以key-val对的方式存储数据
  4. key不能重复,但是值可以重复,允许使用null键和null值
  5. 如果添加相同的key,则会覆盖原来的key-val,等同于修改
  6. 和HashSet一样,不保证映射的顺序,因为底层是以hash表的方式存储的
  7. HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作

Hashtable的基本介绍

  1. 存放键值对K-V
  2. Hashtable的键值都不能为null,否则会抛异常
  3. Hashtable使用的方法基本和HashMap方法一样
  4. Hashtable是线程安全的,HashMap是线程不安全的
  5. 与HashMap比效率较低

TreeSet

Collections工具类

Collections是一个操作Set、List、Map等集合的工具类

排序操作

方法 含义
reverse(List) 反转List中元素的顺序
shuffle(List) 对List集合元素进行随机排序
sort(List) 对List升序排序
sort(List,Comparator) 根据指定的Comparator产生的顺序对List集合元素进行排序
swap(List,int,int) 将指定List集合中的i处元素和j处元素进行交换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

public class Collections_ {
public static void main(String[] args) {
//创建ArrayList 集合,用于测试.
List list = new ArrayList();
list.add("tom");
list.add("smith");
list.add("king");
list.add("milan");
list.add("tom");

// reverse(List):反转 List 中元素的顺序
Collections.reverse(list);
System.out.println("list=" + list);
// shuffle(List):对 List 集合元素进行随机排序
// for (int i = 0; i < 5; i++) {
// Collections.shuffle(list);
// System.out.println("list=" + list);
// }

// sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
Collections.sort(list);
System.out.println("自然排序后");
System.out.println("list=" + list);
// sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
//我们希望按照 字符串的长度大小排序
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//可以加入校验代码.
return ((String) o2).length() - ((String) o1).length();//List允许重复,所以能同时有两个长度相同的
}
});
System.out.println("字符串长度大小排序=" + list);
// swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
//比如
Collections.swap(list, 0, 1);
System.out.println("交换后的情况");
System.out.println("list=" + list);

//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
System.out.println("自然顺序最大元素=" + Collections.max(list));
//Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
//比如,我们要返回长度最大的元素
Object maxObject = Collections.max(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length() - ((String)o2).length();
}
});
System.out.println("长度最大的元素=" + maxObject);

//Object min(Collection)
//Object min(Collection,Comparator)
//上面的两个方法,参考max即可

//int frequency(Collection,Object):返回指定集合中指定元素的出现次数
System.out.println("tom出现的次数=" + Collections.frequency(list, "tom"));

//void copy(List dest,List src):将src中的内容复制到dest中
ArrayList dest = new ArrayList();
//为了完成一个完整拷贝,我们需要先给dest 赋值,大小和list.size()一样
for(int i = 0; i < list.size(); i++) {
dest.add("");
}
//注意:size是指元素个数,而length是数组长度,构造器是不会改变size的。不能通过初始化集合来给定大小,因为这并不会改变size,底层是用两个集合的size比较的,size 和 length 不能等同,size 表示 当前集合的元素个数, 随着元素的增加而增加。

//拷贝
Collections.copy(dest, list);
System.out.println("dest=" + dest);

//boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
//如果list中,有tom 就替换成 汤姆
Collections.replaceAll(list, "tom", "汤姆");
System.out.println("list替换后=" + list);
}
}

删除、查找

方法 含义
Object max 返回最大元素
Object max
Object min
Object min
int frequency
void copy
boolean replaceAll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

public class Collections_ {
public static void main(String[] args) {
//创建ArrayList 集合,用于测试.
List list = new ArrayList();
list.add("tom");
list.add("smith");
list.add("king");
list.add("milan");
list.add("tom");

// reverse(List):反转 List 中元素的顺序
Collections.reverse(list);
System.out.println("list=" + list);
// shuffle(List):对 List 集合元素进行随机排序
// for (int i = 0; i < 5; i++) {
// Collections.shuffle(list);
// System.out.println("list=" + list);
// }

// sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
Collections.sort(list);
System.out.println("自然排序后");
System.out.println("list=" + list);
// sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
//我们希望按照 字符串的长度大小排序
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//可以加入校验代码.
return ((String) o2).length() - ((String) o1).length();//List允许重复,所以能同时有两个长度相同的
}
});
System.out.println("字符串长度大小排序=" + list);
// swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
//比如
Collections.swap(list, 0, 1);
System.out.println("交换后的情况");
System.out.println("list=" + list);

//Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
System.out.println("自然顺序最大元素=" + Collections.max(list));
//Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
//比如,我们要返回长度最大的元素
Object maxObject = Collections.max(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).length() - ((String)o2).length();
}
});
System.out.println("长度最大的元素=" + maxObject);

//Object min(Collection)
//Object min(Collection,Comparator)
//上面的两个方法,参考max即可

//int frequency(Collection,Object):返回指定集合中指定元素的出现次数
System.out.println("tom出现的次数=" + Collections.frequency(list, "tom"));

//void copy(List dest,List src):将src中的内容复制到dest中
ArrayList dest = new ArrayList();
//为了完成一个完整拷贝,我们需要先给dest 赋值,大小和list.size()一样
for(int i = 0; i < list.size(); i++) {
dest.add("");
}
//注意:size是指元素个数,而length是数组长度,构造器是不会改变size的。不能通过初始化集合来给定大小,因为这并不会改变size,底层是用两个集合的size比较的,size 和 length 不能等同,size 表示 当前集合的元素个数, 随着元素的增加而增加。

//拷贝
Collections.copy(dest, list);
System.out.println("dest=" + dest);

//boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
//如果list中,有tom 就替换成 汤姆
Collections.replaceAll(list, "tom", "汤姆");
System.out.println("list替换后=" + list);
}
}

本篇文章参考:

菜鸟教程:https://www.runoob.com/

韩顺平Java基础