1.集合概述

1.1.基本概念

  • 集合可以存储任意类型的对象,并且长度可变

  • 集合对象可以是任意的数据类型,并且长度可变

1.2.集合分类

单列集合Collection双列集合Map
单列集合根接口,用于存储一系列符合某种规则的元素双列集合根接口,用于存储具有键(Key)、值(Value)映射关系的元素
Collection集合有两个重要的子接口,分别是List和SetMap集合中每个元素都包含一对键值,并且Key唯一,在使用Map集合时通过指定的Key找到对应的·
List集合的特点是元素有序、可重复。该接口的主要实现类有ArrayListLinkedListMap接口的主要实现类有HashMapTreeMap
Set集合的特点是元素无序并且不可重复。该接口的主要实现类有HashSetTreeSet

图片1

2.Collection接口

方法声明功能描述
boolean add(Object o)向集合中添加一个元素
boolean addAll(Collection c)将指定集合c中的所有元素添加到该集合中
void clear()删除该集合中的所有元素
boolean remove(Object o)删除该集合中指定的元素
boolean removeAll(Collection c)删除该集合中包含指定集合c中的所有元素
boolean isEmpty()判断该集合是否为空
boolean contains(Object o)判断该集合中是否包含某个元素
boolean containsAll(Collection c)判断该集合中是否包含指定集合c中的所有元素
Iterator iterator()返回在该集合的元素上进行迭代的迭代器(Iterator),用于遍历该集合所有元素
int size()获取该集合元素个数
Streamstream()将集合源转换为有序元素的流对象(JDK 8新方法)

3.List接口

3.1. List接口简介

3.1.1.定义

  • List接口继承自Collection接口,是单列集合的一个重要分支,习惯性的会将实现了List接口的对象称为List集合

3.1.2.特点

  • List集合中允许出现重复元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引(类似于数组中的元素角标)来访问集合中的元素
  • 元素有序存入顺序和取出顺序一致

3.1.3.常用方法

方法声明功能描述
void add(int index,Object element)将元素element插入在List集合的指定索引位置
boolean addAll(int index,Collection c)将集合c包含的所有元素插入到List集合的指定索引位置
Object get(int index)返回集合索引index处的元素
Object remove(int index)删除index索引处的元素
Object set(int index, Object element)将索引index处元素替换成element元素,并将替换后的元素返回
int indexOf(Object o)返回对象o在List集合中首次出现的位置索引
int lastIndexOf(Object o)返回对象o在List集合中最后一次出现的位置索引
List subList(int fromIndex, int toIndex)返回从索引fromIndex(包括)到 toIndex(不包括)处所有元素集合组成的子集合
Object[] toArray()将集合元素转换为数组
default void sort(Comparator<? super E> c)根据指定的比较器规则对集合元素排序(JDK 8新方法)

3.2. ArrayList类

3.2.1.特点

  • ArrayList是List接口的一个实现类,它是程序中最常见的一种集合
  • ArrayList内部的数据存储结构是数组形式
  • 不适合做大量的增删操作
  • 遍历和查找元素时显得非常高效

3.2.2.语法

1
2
3
4
5
ArrayList list = new ArrayList();
list.add("stu1");
list.add("stu2");
System.out.println("集合的长度:" + list.size());
System.out.println("第2个元素是:" + list.get(1));

3.3. LinkedList类

3.3.1.特点

  • 内部包含有两个Node类型的first和last属性的双向循环链表结构
  • 遍历和查找效率较低
  • 增删操作表现出很高的效率

3.3.2.底层原理

图片2

  • 左图为新增元素,图中的元素1和元素2在集合中彼此为前后关系,在它们之间新增一个元素时,只需要让元素1记住它后面的元素是新元素,让元素2记住它前面的元素为新元素就可以了

  • 右图为删除元素,要想删除元素1和元素2之间的元素3,只需要让元素1与元素2变成前后关系就可以了

3.3.3.特有方法

方法声明功能描述
void add(int index, E element)在此列表中指定的位置插入指定的元素。
void addFirst(Object o)将指定元素插入集合的开头
void addLast(Object o)将指定元素添加到集合的结尾
Object getFirst()返回集合的第一个元素
Object getLast()返回集合的最后一个元素
Object removeFirst()移除并返回集合的第一个元素
Object removeLast()移除并返回集合的最后一个元素
boolean offer(Object o)将指定元素添加到集合的结尾
boolean offerFirst(Object o)将指定元素添加到集合的开头
boolean offerLast(Object o)将指定元素添加到集合的结尾
Object peek()获取集合的第一个元素
Object peekFirst()获取集合的第一个元素
Object peekLast()获取集合的最后一个元素
Object poll()移除并返回集合的第一个元素
Object pollFirst()移除并返回集合的第一个元素
Object pollLast()移除并返回集合的最后一个元素
void push(Object o)将指定元素添加到集合的开头
Object pop()移除并返回集合的第一个元素

3.3.4.语法

1
2
3
4
5
6
7
8
LinkedList link = new LinkedList();  
link.add("stu1");
link.add("stu2");
link.offer("offer"); // 向集合尾部追加元素
link.push("push"); // 向集合头部添加元素
Object object = link.peek(); //获取集合第一个元素
link.removeFirst(); // 删除集合第一个元素
link.pollLast(); // 删除集合最后一个元素

4.Collection集合遍历

4.1. Iterator遍历集合

4.1.1.工作原理

图片3

  • Iterator遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用next()方法之前,索引位于第一个元素之前,不指向任何元素

  • 第一次调用next()方法后,索引会向后移动一位,指向第一个元素并将该元素返回

  • 再次调用next()方法时,索引会指向第二个元素并将该元素返回

  • 以此类推,直到hasNext()方法返回false,表示到达了集合的末尾终止对元素的遍历

4.1.2.实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int[] arr = new int[10];
ArrayList<Integer> list = new ArrayList<Integer>();

for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(100) + 1;
list.add(arr[i]);
}
System.out.println(Arrays.toString(arr));
System.out.println(list);

/**
* 使用Iterator迭代器遍历list集合
*/
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}

4.1.3.注意

  • Iterator迭代器对集合中的元素进行迭代时,如果调用了集合对象的remove()方法删除元素,会出现ConcurrentModificationException异常。

4.2. foreach遍历集合

1
2
3
4
5
6
/**
* 使用增强版for遍历list集合
*/
for (Object obj : list) {
System.out.println(obj);
}
  • 注意:foreach循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改

4.3. JDK8 —- foreach遍历集合

1
list.forEach(obj -> System.out.println(obj));

4.4.forEachRemaining

  • JDK 8中还针对Iterator迭代器对象提供了一个forEachRemaining(Consumer action)方法来进行遍历,该方法同样需要一个函数式接口
1
2
Iterator it = list.iterator();
it.forEachRemaining(obj -> System.out.println("迭代集合元素:"+obj));

5.Set接口

5.1. Set接口简介

5.1.1.特点

  • Set接口和List接口一样,同样继承自Collection接口
  • Set接口中的元素无序,并且都会以某种规则保证存入的元素不出现重复

5.1.2.分类

分类简介
HashSet根据对象的哈希值来确定元素在集合中的存储的位置,因此具有良好的存取和查找性能
TreeSet以二叉树的方式来存储元素,它可以实现对集合中的元素进行排序

5.2. HashSet类

5.2.1.特点

  • HashSet是Set接口的一个实现类,它所存储的元素不可重复,并且无序
  • 当向HashSet集合中添加一个元素时,首先会调用该元素的hashCode()方法来确定元素的存储位置,然后再调用元素对象的equals()方法来确保该位置没有重复元素

5.2.2.存储原理

set

  • 在Java中,一些基本数据包装类、String类等都已经默认重写了hashCode()equals()方法
  • 开发者向HashSet集合中添加自定义的数据类型,如Student类时,必须增加重写的hashCode()equals()方法,才能保证数据的唯一性。

5.3. TreeSet类

5.3.1.特点

  • TreeSetSet接口的另一个实现类,它内部采用平衡二叉树来存储元素,来保证TreeSet集合中没有重复的元素,并且可以对元素进行排序
  • 二叉树就是每个节点最多有两个子节点的有序树,每个节点及其子节点组成的树称为子树,左侧的节点称为“左子树”,右侧的节点称为“右子树”,其中左子树上的元素小于它的根结点,而右子树上的元素大于它的根结点

5.3.2.存储原理

TreeSet存储原理

treeSet存储原理2

5.3.3.特有方法

方法声明功能描述
Object first()返回TreeSet集合的首个元素
Object last()返回TreeSet集合的最后一个元素
Object lower(Object o)返回TreeSet集合中小于给定元素的最大元素,如果没有返回null
Object floor(Object o)返回TreeSet集合中小于或等于给定元素的最大元素,如果没有返回null
Object higher(Object o)返回TreeSet集合中大于给定元素的最小元素,如果没有返回null
Object ceiling(Object o)返回TreeSet集合中大于或等于给定元素的最小元素,如果没有返回null
Object pollFirst()移除并返回集合的第一个元素
Object pollLast()移除并返回集合的最后一个元素

5.3.4.排序

5.3.4.1.基本概念
  • 向TreeSet集合添加元素时,都会调用compareTo()方法进行比较排序,该方法是Comparable接口中定义的,因此要想对集合中的元素进行排序,就必须实现Comparable接口
  • Java中大部分的类都实现了Comparable接口,并默认实现了接口中的CompareTo()方法,如IntegerDouble和String
5.3.4.2.分类
  • 自然排序:要求存储的元素类必须实现Comparable接口,并重写compareTo()方法
  • 定制排序:要求自定义一个比较器,该比较器必须实现Comparator接口,并重写compare()方法,然后将该比较器作为参数传入集合的有参构造
5.3.4.2.主要区别
自然排序定制排序
适合元素类本身未实现Comparable接口,无法进行比较元素类本身实现Comparable接口
适合元素类实现的Comparable接口排序规则无法满足用户需求依赖compareTo()方法的实现
会额外定义一个实现Comparator接口的比较器实现Comparable接口排序规则比较单一,不利于后续改进
5.3.4.2.实现
  • 自然排序
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
public class Person implements Comparable<Object>{
public String name;
public int age;

public Person() {
super();
}

public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}

public String toString() {
return this.name + ": " + age;
}

public int compareTo(Object obj) {
Person person = (Person) obj;
if(this.age - person.age > 0) {
return 1;
}
if(this.age - person.age == 0) {
return this.name.compareTo(person.name);
}
return -1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.TreeSet;

public class Main {
public static void main(String[] args) {
TreeSet<Object> ts = new TreeSet<Object>();
ts.add(new Person("Alice", 13));
ts.add(new Person("Bob", 23));
ts.add(new Person("Charli", 13));
ts.add(new Person("Alice", 13));
System.out.println(ts);
}
}
  • 定制排序
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
import java.util.Comparator;

public class Person implements Comparator<Object>{
public String name;
public int age;

public Person() {
super();
}

public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}

public String toString() {
return this.name + ": " + age;
}

public int compare(Object obj1, Object obj2) {
Person person1 = (Person) obj1;
Person person2 = (Person) obj2;
if(person1.age - person2.age > 0) {
return 1;
}
if(person1.age - person2.age == 0) {
return person1.name.compareTo(person2.name);
}
return -1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.TreeSet;

public class Main {
public static void main(String[] args) {
TreeSet<Object> ts = new TreeSet<Object>(new Person());
ts.add(new Person("Alice", 13));
ts.add(new Person("Bob", 23));
ts.add(new Person("Charli", 13));
ts.add(new Person("Alice", 13));
System.out.println(ts);
}
}

6.Map接口

6.1. Map接口简介

6.1.1.特点

  • 双列集合,它的每个元素都包含一个键对象Key和值对象Value,键和值对象之间存在一种对应关系,称为映射
  • Map中的映射关系是一对一的,一个键对象Key对应唯一一个值对象Value,其中键对象Key和值对象Value可以是任意数据类型,并且键对象Key不允许重复,这样在访问Map集合中的元素时,只要指定了Key,就能找到对应的Value

6.1.2.常用方法

方法声明功能描述
void put(Object key, Object value)向Map集合中添加指定键值映射的元素
int size()返回Map集合键值对映射的个数
Object get(Object key)返回指定键所映射的值,如果此映射不包含该键的映射关系,则返回null
boolean containsKey(Object key)查看Map集合中是否存在指定的键对象key
boolean containsValue(Object value)查看Map集合中是否存在指定的值对象value
Object remove(Object key)删除并返回Map集合中指定键对象Key的键值映射元素
void clear()清空整个Map集合中的键值映射元素
Set keySet()以Set集合的形式返回Map集合中所有的键对象Key
Collection values()以Collection集合的形式返回Map集合中所有的值对象Value
Set<Map.Entry<Key,Value>> entrySet()将Map集合转换为存储元素类型为Map的Set集合
Object getOrDefault(Object key, Object defaultValue)返回Map集合指定键所映射的值,如果不存在则返回默认值defaultValue(JDK 8新方法)
void forEach(BiConsumer action)通过传入一个函数式接口对Map集合元素进行遍历(JDK 8新方法)
Object putIfAbsent(Object key, Object value)向Map集合中添加指定键值映射的元素,如果集合中已存在该键值映射元素,则不再添加而是返回已存在的值对象Value(JDK 8新方法)
boolean remove(Object key, Object value)删除Map集合中键值映射同时匹配的元素(JDK 8新方法)
boolean replace(Object key, Object value)将Map集合中指定键对象Key所映射的值修改为value(JDK 8新方法)

6.2. HashMap类

6.2.1.特点

  • HashMap集合是Map接口的一个实现类,它用于存储键值映射关系,该集合的键和值允许为空,但键不能重复,且集合中的元素是无序
  • HashMap底层是由哈希表结构组成的,其实就是“数组+链表”的组合体,数组是HashMap的主体结构,链表则主要是为了解决哈希值冲突而存在的分支结构。正因为这样特殊的存储结构,HashMap集合对于元素的增、删、改、查操作表现出的效率都比较高

6.2.2.内部结构

HashMap内部结构

  • 在哈希表结构中,主体结构为图中水平方向的数组结构,其长度称为HashMap集合的容量(capacity)
  • 数组结构垂直对应的是链表结构,链表结构称为一个桶(bucket),每个桶的位置在集合中都有对应的桶值,用于快速定位集合元素添加、查找时的位置

6.2.3.存储原理

HashMap存储原理

6.2.4.注意

  • 使用HashMap集合时,如果通过键对象k定位到的桶位置不含链表结构,那么对于查找、添加等操作很快;如果定位到的桶位置包含链表结构,对于添加操作,其时间复杂度依然不大,因为最新的元素会插入链表头部,只需要简单改变引用链即可;而对于查找操作来讲,此时就需要遍历链表,然后通过键对象k的equals(k)方法逐一查找比对。

    所以,从性能方面考虑,HashMap中的链表出现越少,性能才会越好,这就要求HashMap集合中的桶越多越好。

  • HashMap根据实际情况,内部实现了动态地分配桶数量的策略。

    通过new HashMap()方法创建HashMap时,会默认集合容量capacity大小为16,加载因子loadFactor为0.75(HashMap桶多少权衡策略的经验值),此时该集合桶的阀值就为12(容量capacity与加载因子loadFactor的乘积),如果向HashMap集合中不断添加完全不同的键值对<k,v>,当超过12个存储元素时,HashMap集合就会默认新增加一倍桶的数量(也就是集合的容量),此时集合容量就变为32。

6.2.4.LinkedHashMap

  • HashMap集合并不保证集合元素存入和取出的顺序
  • 如果想让这两个顺序一致,可以使用LinkedHashMap类,它是HashMap的子类。和LinkedList一样也使用双向链表来维护内部元素的关系,使LinkedHashMap元素迭代的顺序与存入的顺序一致
  • 一般情况下,用的最多的是HashMap,在Map中插入、删除和定位元素,HashMap 是最好的选择。但如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列

6.3. Map集合遍历

6.3.1.Iterator迭代器

  • 遍历思路:先将Map集合转换为Iterator接口对象,然后进行遍历。由于Map集合中元素是由键值对组成的,所以使用Iterator接口遍历Map集合时,会有两种将Map集合转换为Iterator接口对象再进行遍历的方法
  • 遍历方法:keySet()方法和entrySet()方法
6.3.1.1.keySet()方法
  • 先将Map集合中所有键对象转换为Set单列集合,接着将包含键对象的Set集合转换为Iterator接口对象,然后遍历Map集合中所有的键,再根据键获取相应的值。
1
2
3
4
5
6
7
Set keySet = map.keySet();           // 获取键的集合
Iterator it = keySet.iterator(); // 迭代键的集合
while (it.hasNext()) {
Object key = it.next();
Object value = map.get(key); // 获取每个键所对应的值
System.out.println(key + ":" + value);
}
6.3.1.2.entrySet()方法
  • 将原有Map集合中的键值对作为一个整体返回为Set集合,接着将包含键值对对象的Set集合转换为Iterator接口对象,然后获取集合中的所有的键值对映射关系,再从映射关系中取出键和值
1
2
3
4
5
6
7
8
Set entrySet = map.entrySet();
Iterator it = entrySet.iterator(); // 获取Iterator对象
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) (it.next());
Object key = entry.getKey(); // 获取Entry中的键
Object value = entry.getValue(); // 获取Entry中的值
System.out.println(key + ":" + value);
}

6.3.2.JDK8提供的forEach

  • JDK 8中,根据Lambda表达式特性新增了一个forEach(BiConsumer action)方法来遍历Map集合,该方法所需要的参数也是一个函数式接口,因此可以使用Lambda表达式的书写形式来进行集合遍历
1
map.forEach((key,value) -> System.out.println(key + ":" + value));

6.3.3.值遍历—values()方法

  • Map集合中,除了以上介绍的两种主要的遍历方式外,还提供了一个values()方法,通过这个方法可以直接获取Map中存储所有值的Collection集合
1
2
Collection values = map.values(); // 获取Map集合中value值集合对象
values.forEach(v -> System.out.println(v));

6.4. TreeMap集合

  • 介绍: TreeMap集合是Map接口的另一个实现类,在TreeMap内部是通过二叉树的原理来保证键的唯一性,这与TreeSet集合存储的原理一样,因此TreeMap所有的键是按照某种顺序排列的
  • 说明:为了实现TreeMap元素排序,可以参考TreeSet 集合排序方式,使用自然排序和定制排序
1
2
3
4
5
6
7
8
9
10
11
import java.util.Map;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
Map map = new TreeMap();
map.put("2", "Rose");
map.put("1", "Jack");
map.put("3", "Luck");
System.out.println(map);
}
}

comparator方式

1
2
3
4
5
6
7
8
import java.util.*;
class CustomComparator implements Comparator {
public int compara (Object obj1, Object obj2) {
String key1 = (String) obj1;
String key2 = (String) obj2;
return key2.comparaTo(key1); //调用了String对象的comparaTo()方法
}
}
1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
Map map = new TreeMap(new CustomComparator());
map.put("2", "Rose");
map.put("1", Jack);
map.put("3", "Luck");
System.out.println(map);
}
}
1
2
3
4
/**
* 打印结果
* {1=Jack, 2=Rose, 3=Luck}
*/

6.5. Properties集合

  • 介绍: Map接口还有一个实现类Hashtable,它和HashMap十分相似,其中一个主要区别在于Hashtable是线程安全的
  • 说明:Hashtable类有一个子类PropertiesProperties主要用来存储字符串类型的键和值,在实际开发中,经常使用**Properties集合类来存取应用的配置项**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;

puclic class Main {
public static void main(String[] args) throws Exception {
// 1.通过Properties进行属性文件读取操作
Properties pps = new Properties();
// 加载读取的文件 test.properties
pps.load(new FileInputStream("test.properties"));
// 遍历test.properties键值对元素信息
pps.forEach((key, value) -> System.out.println(key + " = " + value));
// 2.通过Properties进行属性文件写入操作
// 指定写入操作的文件名称和位置
FileOutputStream out = new FileOutputStream("test.properties");
// 向Properties类文件进行写入键值对信息
pps.setProperties("charset", "utf-8");
// 将此 Properties 集合中新增键值对信息写入配置文件
pps.store(out, "新增charset编码");
}
}
1
2
3
4
5
6
7
/**
* 打印结果
*
* language=chinese
* Font-size=14px
* Backgroup-color=red
*/

7.泛型

7.1.why

  • 集合中可以存储任意类型的对象元素,但是当把一个对象存入集合后,集合会“忘记”这个对象的类型,将该对象从集合中取出时,这个对象的编译类型就统一变成了Object类型
  • 在程序中无法确定一个集合中的元素到底是什么类型,那么在取出元素时,如果进行强制类型转换就很容易出错

7.2.语法

1
2
ArrayList<参数化类型> list = new ArrayList<参数化类型>();
ArrayList<String> list = new ArrayList<String>();

8.常用工具类

8.1. Collections工具类

8.1.1.添加、排序常用方法

方法声明功能描述
staticboolean addAll(Collection<? super T> c, T… elements)将所有指定元素添加到指定集合c中
static void reverse(List list)反转指定List集合中元素的顺序
static void shuffle(List list)对List集合中的元素进行随机排序
static void sort(List list)根据元素的自然顺序对List集合中的元素进行排序
static void swap(List list,int i,int j)将指定List集合中角标i处元素和j处元素进行交换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
improt java.util.ArrayList;
import java.util.Collections;

public class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "c", "z", "b", "k");
System.out.println("排序前:" + list);
Collections.reverse(list);
System.out.println("反转后:" + list);
Collections.sort(list);
System.out.println("按自然顺序排序后:" + list);
Collections.shuffle(list);
System.out.print("随机排序后:" + list);
Collections.swap(list, 0, list.size() - 1);
System.out.print("集合收尾交换后:" + list);
}
}
1
2
3
4
5
6
7
8
9
/**
* 打印结果
*
* 排序前:[c, z, b, k]
* 反转后:[k, b, z, c]
* 按自然顺序排序后:[b, c, k, z]
* 按随机顺序排序后:[b, z, k, c]
* 集合收尾元素交换后:[c, z, k, b]
*/

8.1.2.查找、替换常用方法

方法声明功能描述
static int binarySearch(List list,Object key)使用二分法搜索指定对象在List集合中的索引,查找的List集合中的元素必须是有序的
static Object max(Collection col)根据元素的自然顺序,返回给定集合中最大的元素
static Object min(Collection col)根据元素的自然顺序,返回给定集合中最小的元素
static boolean replaceAll(List list,Object oldVal,Object newVal)用一个新值newVal替换List集合中所有的旧值oldVal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
improt java.util.ArrayList;
import java.util.Collections;

public class Main {
public static void main(String[] args) {
ArrayList<Integer>list = new ArrayList<Integer>();
Collections.addAll(list, -3, 2, 9, 5, 8);
System.out.println("集合中的元素:" + lsit);
System.out.println("集合中最大元素:" + Collections.max(list));
Systme.out.println("集合中最小元素:" + Collections.min(list));
Collections.replaceAll(list, 8, 0); //将集合中的8用0替换掉
System.out.println("替换后的集合:" + list);
collections.sort(list); //使用二分法查找前,必须保证元素有序
System.out.println("集合排序后为:" + list);
int index = Collections.binarySearch(list, 9);
System.out.println("集合通过二分查找方法查找元素9所在角标为:" + index);
}
}
1
2
3
4
5
6
7
8
9
10
/**
* 打印结果
*
* 集合中的元素:[-3, 2, 9, 5, 8]
* 集合中的最大元素:9
* 集合中的最小元素:-3
* 替换后的集合:[-3, 2, 9, 5, 0]
* 集合排序后为:[-3, 0, 2, 5, 9]
* 集合通过二分查找方法查找元素9所在角标为:4
*/

8.2. Arrays工具类

  • sort() 排序
1
2
3
4
5
int[] arr = {9, 8, 3, 5, 2};
Arrays.sort(arr);
System.out.println(arr);

// {2, 3, 5, 8, 9}
  • binarySearch(Object[] obj, Object key) 用二分法查找obj中的key
1
2
3
4
5
6
7
8
int[] arr = {9, 8, 3, 5, 2};
Arrays.sort(arr);
System.out.println(arr);
int index = Arrays.binarySearch(arr, 8);
System.out.println("8的索引位为:" + index);

// {2, 3, 5, 8, 9}
// 8的索引位为:3
  • copyOfRange(int[] original, int from, int to) 复制数组的指定范围
1
2
3
4
5
int[] arr = {9, 8, 3, 5, 2}
int[] copy = Arrays.copyOfRange(arr, 1, 7);
System.out.println(copy);

// {8, 3, 5, 2}
  • fill(Object[] a, Object value)value把数组填充
1
2
3
4
int[] arr = {9, 8, 3, 5, 2};
Arrays.fill(arr, 6);

// {6, 6, 6, 6, 6}
  • asList() 把Array转换为List
1
2
3
4
5
Integer[] array = {9, 8, 3, 5, 2};
List<Integer> list = Arrays.toList(array);
System.out.println(list);

// [9, 8, 3, 5, 2]
  • stream() 创建stream流对象
1
2
3
4
Integer[] array = {9, 8, 3, 5, 2};
Stream<Integer> stream = Arrays.stream(array);
stream.forEach(item -> System.out.println(item + " "));
// 9 8 3 5 2

9.JDK8 —- 聚合操作

9.1. 聚合操作简介

聚合

9.2. 创建Stream流对象

  • 所有的Collections集合都可以使用stream()静态方法获取Stream流对象
  • Stream接口的of()静态方法可以获取基本类型包装类数组、引用类型数组和单个元素的Stream流对象
  • Arrays工具类的stream()静态方法也可以获取数组元素的Stream流对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.*;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Integer array = {9, 8, 3, 5, 2};
List<Integer> list = Arrays.asList(array);

// 1.使用stream()静态方法获取Stream流对象
Stream<Integer> stream1 = list.stream();
stream1.forEach(item -> System.out.println(item + " "));
System.out.println();

// 2.使用Stream接口的of()静态方法创建Stream流对象
Stream<Integer> stream2 = Stream.of(array);
stream2.forEach(item -> System.out.println(item + " "));
System.out.println();

// 3.使用Arrays数组工具类的stream()静态方法创建stream流对象
Stream<Integer> stream3 = Arrays.stream(array);
stream3.forEach(item -> System.out.println(item + " "));
}
}
  • 注意:在进行聚合操作时,只是改变了Stream流对象中的数据,并不会改变原始集合或数组中的源数据

9.3. Stream流的常用方法

方法声明功能描述
Streamfilter(Predicate<? super T> predicate)将指定流对象中的元素进行过滤,并返回一个子流对象
Streammap(Function<? super T, ? extends R> mapper)将流中的元素按规则映射到另一个流中
Streamdistinct()删除流中重复的元素
Streamsorted()将流中的元素按自然顺序排序
Streamlimit(long maxSize)截取流中元素的长度
Streamskip(long n)丢弃流中前n个元素
staticStreamconcat(Stream<? extends T> a, Stream<? extends T> b)将两个流对象合并为一个流
long count()统计流中元素的个数
R collect(Collector<? super T, A, R> collector)将流中的元素收集到一个容器中(如集合)
Object[] toArray()将流中的元素收集到一个数组中
void forEach(Consumer<? super T> action)将流中的元素进行遍历
  • forEach() 遍历
1
2
3
// 不保证元素的遍历过程在流中是被有序执行的
void forEach(Consumer<? super T> action);
stream.forEach(i -> System.out.println(i)); // 遍历并打印流元素
  • filter() 过滤
1
2
3
// 将一个Stream流中的元素进行筛选转换成另一个子集流
Stream<T>filter(Predicate<? super T> predicate);
stream.filter(i -> i.startsWith("张")); // 筛选出以“张”开头的流元素
  • map() 映射
1
2
3
4
// 将流对象中的元素通过特定的规则进行修改然后映射为另一个流对象
Stream<R> map(Function<? super T, ? extends R> mapper);
stream.map(str -> str.toUpperCase());
stream.map(String::toUpperCase); // 将所有流元素字母转换为大写
  • limit() 截取
1
2
3
4
5
// 用于对流对象中的元素进行截取操作
// 在多数情况下,limit()方法会与skip()方法(跳过方法)组合使用,用于截取流对象中指定位置的多个元素
Stream<T> limit(long maxSize);
stream.skip(1) // 跳过流中的前1个元素
.limit(2); // 截取剩余流中的前2个元素
  • collect() 收集
1
2
3
4
5
6
7
8
9
10
11
12
13
// 
/**
* collect()是十分有用的终结操作,它可以把Stream中的元素保存为另外一种形式,比如集合、字符串等
*
* Collector参数包含4种操作
*
* 1) supplier(初始构造器)
* 2) accumulator(累加器)
* 3) combiner(组合器)
* 4)finisher(终结者)
* 在JDK 8中的java.util.stream包下的Collectors类内置了各种复杂的收集操作
*/
<R, A> R collect(Collector<? super T, A, R> collector);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("张三", "李四", "张小明", "张阳");
// 通过filter()方法筛选出字符串中以“张”开头的元素
// 通过collect()方法进行终结操作,将流元素收集到一个List集合中
List<String> list = stream1.filter(item -> item.startWith("张"))
.collect(Collectors.toList());
System.out.println(list);

Stream<String> stream2 = Stream.of("张三", "李四", "张小明", "张阳");
// 通过filter()方法筛选出字符串中以“张”开头的元素
// 通过collect()方法进行终结操作,将流元素使用“and”连接收集到一个字符串中
String string = stream2.filter(item -> item.startWith("张"))
.collect(Collectors.joining("and"));
System.out.println(string);
}
}
1
2
3
4
5
6
/**
* 打印结果
*
* [张三, 张小明, 张阳]
* 张三 and 张小明 and 张阳
*/

9.4. Parallel Stream (并行流)

9.4.1.串并流对比

  • 串行流(Serial Stream):将源数据转换为一个流对象,然后在单线程下执行聚合操作的流(也就是单一管道流)
  • 并行流(Parallel Stream):将源数据分为多个子流对象进行多线程操作(也就是多个管道流),然后将处理的结果再汇总为一个流对象

串并流

9.4.2.并行流创建

  • 通过Collection集合接口的parallelStream()方法直接将集合类型的源数据转变为Stream并行流
  • 通过BaseStream接口的parallel()方法将Stream串行流转变为Stream并行流
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.*;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<String> list = Arrays.asList("张三", "李四", "张小明", "张阳");

// 1.直接使用Collection接口的parallelStream()创建并行流
Stream<String> paralleStream = list.parallelStream();
System.out.println(parallelStream.isParallel());

// 创建一个Stream串行流
Stream<String> stream = Stream.of("张三", "李四", "张小明", "张阳");
// 2.使用BaseStream接口的parallel()方法将串行流变为并行流
Stream<String> parallel = stream.parallel();
System.out.println(parallel.isParallel());
}
}
1
2
3
4
5
/**
* 打印结果
* true
* true
*/

9.4.3.注意

  • 创建Stream流对象时,除非有特别声明,否则默认创建的都是串行流

  • 使用Stream并行流在一定程度上可以提升程序的执行效率,但是在多线程执行就会出现线程安全这个大问题,所以为了能够在聚合操作中使用Stream并行流,前提是要执行操作的源数据在并行执行过程中不会被修改