一、Collections
1、可变参数
定义:是一种特殊形参,定义在方法、构造器的形参列表里,定义格式是:方法名(数据类型... 形参名称){ }
可变参数的特点和好处
特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它;
好处:常常用来灵活的接收数据。
注意事项
可变参数在方法内部就是一个数组
一个形参列表中可变参数只能有一个
可变参数必须放在形参列表的最后面
public class Demo {
public static void main(String[] args) {
add(); //可以不传递参数
}
public static void add(int...nums) { //可变参数必须写在参数列表的最后
int sum = 0;
for (int num : nums) {
sum += num;
}
System.out.println("sum=" + sum);
}
}
2、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集合中的元素进行升序排序 |
public static <T> void sort(List<T> list,Comparator<? super T> c) | 对List集合中元素,按照比较器对象指定的规则进行排序 |
举例子:
public class Demo {
public static void main(String[] args) {
//static <T> boolean addAll(单列集合,可变参数) 批量添加元素
List<String> list = new ArrayList<>();
Collections.addAll(list, "丁真", "雪豹", "理塘");
System.out.println(list);
System.out.println("==========================");
//static void shuffle(List集合) 打乱List集合元素顺序,每次调用都会打乱
Collections.shuffle(list);
System.out.println(list);
System.out.println("==========================");
//static <T> void sort(List集合) List集合进行自然排序
List<Integer> nums = new ArrayList<>();
Collections.addAll(nums, 99, 55, 67, 3);
Collections.sort(nums);
System.out.println(nums);
System.out.println("==========================");
//排自定义类对象,需要指定排序规则
List<Student> stuList = new ArrayList<>();
stuList.add(new Student("zhangsan", 18));
stuList.add(new Student("wangwu", 22));
stuList.add(new Student("zhaoliu", 21));
stuList.add(new Student("lisi", 19));
stuList.add(new Student("qianqi", 20));
//static<T> void sort(List集合,比较器);List集合进行比较器排序
Collections.sort(stuList, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge(); //按照年龄age从小到大排列
}
});
for (Student student : stuList) {
System.out.println(student);
}
}
}
class Student { ... }
输出效果
二、Map集合
1、概述
- Map集合称为双列集合,一次需要存一对数据做为一个元素,格式:{key1=value1 , key2=value2 , key3=value3 , ...}
- Map集合的每个元素分为两部分:key和value,key称为键,value称为值,整体叫键值对,因此Map也叫“键值对集合”
- Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值
在需要存储一一对应的数据时,就可以考虑使用Map集合来做,例如购物网站中,商品编号和商品数量一一对应
Map集合体系
Map集合体系的特点
注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的
- HashMap: 无序、不重复 (用的最多)
- LinkedHashMap :有序、不重复
- TreeMap: 按照大小默认升序排序、不重复
2、常用方法
- Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。
Map常用方法:
方法名 | 说明 |
public V put(K key,V value) | 添加/修改元素 |
public int size() | 获取集合的大小 |
public void clear() | 清空集合 |
public boolean isEmpty() | 判断集合是否为空 |
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集合的全部值 |
举例子:
package com.itheima.c_map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
Map
双列集合根接口,它的功能是全部双列集合都可以继承过来使用的。
常用方法
V put(K key,V value) 添加元素
int size() 获取集合的大小
boolean isEmpty() 判断集合是否为空,为空返回true, 反之
V get(Object key) 根据键获取对应值
V remove(Object key) 根据键删除整个元素
boolean containsKey(Object key) 判断是否包含某个键
boolean containsValue(Object value) 判断是否包含某个值
Set<K> keySet() 获取全部键的集合
Collection<V> values() 获取Map集合的全部值
void clear() 清空集合
*/
public class Demo1 {
public static void main(String[] args) {
//用多态的方式创建一个HashMap
Map<String,String> map = new HashMap<>();
//向map中存入西游四人组的编号和姓名 001-玄奘 002-悟空 003-悟能 004-悟净
//V put(K key,V value) 添加元素
map.put("001","玄奘");
map.put("002","悟空");
map.put("003","悟能");
map.put("004","吴京");
System.out.println(map);
System.out.println("======================");
//int size() 获取集合的大小
System.out.println(map.size());
System.out.println("======================");
//boolean isEmpty() 判断集合是否为空,为空返回true, 反之
System.out.println(map.isEmpty());
System.out.println("======================");
//V get(Object key) 根据键获取对应值
String value = map.get("004");
System.out.println(value);
System.out.println("======================");
//V remove(Object key) 根据键删除整个元素
map.remove("003");
System.out.println(map);
System.out.println("======================");
//boolean containsKey(Object key) 判断是否包含某个键
boolean containsKey = map.containsKey("004");
System.out.println(containsKey);
System.out.println("======================");
//boolean containsValue(Object value) 判断是否包含某个值
boolean containsValue = map.containsValue("吴京");
System.out.println(containsValue);
System.out.println("======================");
//Set<K> keySet() 获取全部键的集合
Set<String> keys = map.keySet();
System.out.println(keys);
System.out.println("======================");
//Collection<V> values() 获取Map集合的全部值
Collection<String> values = map.values();
System.out.println(values);
System.out.println("======================");
//void clear() 清空集合
map.clear();
System.out.println(map);
}
}
tips:map中的key没有顺序,本例中的“001”,“002”之所以输出时按照顺序排列,是因为这几组数字序号的哈希值恰好是连在一起的顺序排列,map在默认情况下根据他们的哈系值顺序添加
输出效果
3、遍历方式
方式一
先获取Map集合全部的键,再通过遍历键来找值
//遍历方式1: 先获取Map集合全部的键,再通过遍历键来找值
private static void test1(HashMap<String, String> map) {
Set<String> keys = map.keySet();
for (String key : keys) {
String value = map.get(key);
System.out.println(key + "---" + value);
}
}
方式二
把“键值对”看成一个整体进行遍历
获取所有的entry对象,从里面拿到 key/value
Map提供的方法
Set<Map.Entry<K, V>> entrySet() 获取所有“键值对”的集合
Map.Entry提供的方法 | 说明 |
K getKey() | 获取键 |
V getValue() | 获取值 |
//遍历方式2: 将map中的所有键值对放入一个set集合中, 然后遍历set集合拿到每个键值对, 再取里面的键值
private static void test2(HashMap<String, String> map) {
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "---" + value);
}
}
方式三
结合lambda遍历Map集合
default void forEach(BiConsumer<? super K, ? super V> action)
//遍历方式3: Lambda, 使用foreach(BiConsumer bc)
private static void test3(HashMap<String, String> map) {
map.forEach((k, v) -> {
System.out.println(k + "---" + v);
});
}
4、HashMap
前面提到HashMap的特点 :无序、不重复
底层原理
HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的
实际上Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。
保存数据到HashMap中,是将键和值保存Entry对象,然后基于哈希表计算将其保存到底层的数组的对应位置。
同样的,HashMap依赖hashCode方法和equals方法保证键的唯一。 如果键要存储的是自定义对象,需要重写hashCode和equals方法。、
5、LinkedHashMap
LinkedHashMap的特点是 : 有序、不重复
底层原理
底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双向链表的机制记录元素顺序(保证有序)
实际上LinkedHashSet集合的底层原理就是LinkedHashMap。
6、TreeMap
TreeMap的特点是:不重复、无索引、可排序(按照键的大小默认升序排列,只能对键排序)
底层原理
TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
TreeMap集合同样也支持两种方式来指定排序规则
- 让类实现Comparable接口,重写比较规则
- TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则
三、Stream
1、定义
Stream也叫Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。
优势: Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好(?
使用步骤:
- 先得到集合或者数组的Stream流(获取)
- 然后调用Stream流的方法对数据进行处理(操作)
- 获取处理的结果(终结)
2、常用方法
获取Stream流的方法
获取单列集合 的Stream流
default Stream<E> stream()
Collection提供的方法,获取当前集合对象的Stream流
获取数组的Stream流
public static <T> Stream<T> stream(T[] array)
Arrays提供的方法,获取当前数组的Stream流
获取零散数据的Stream流(不常用)
public static<T> Stream<T> of(T... values)
Stream类提供的方法,获取当前接收数据的Stream流
Stream常见的中间方法
中间方法指的是对stream流进行操作的方法, 他们调用完成后会返回新的Stream流,可以继续使用(支持链式编程)
Stream提供的常用中间方法 | 说明 |
Stream<T> filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤 |
Stream<T> sorted() | 对元素进行升序排序 |
Stream<T> sorted(Comparator<? super T> comparator) | 按照指定规则排序(老朋友比较器) |
Stream<T> limit(long maxSize) | 获取前几个元素 |
Stream<T> skip(long n) | 跳过前几个元素 |
Stream<T> distinct() | 去除流中重复的元素 |
<R> Stream<R> map(Function<? super T,? extends R> mapper) | 对元素进行加工,并返回对应的新流 |
static <T> Stream<T> concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
举例子
public class Demo {
public static void main(String[] args) {
List<Integer> list = List.of(61, 57, 66, 77, 88, 44, 100, 89, 97, 47, 70);
//需求1: 找出所有及格的分数,并打印
System.out.println("1=================");
list.stream()
.filter(e -> e>= 60) //过滤操作
.forEach(System.out::println); //引用System.out的println方法
//需求2: 找出所有及格的分数, 正序排列, 打印输出
System.out.println("2=================");
list.stream()
.filter(e -> e >= 60) //过滤
.sorted() //升序排序
.forEach(System.out::println);
//需求3: 找出所有及格的分数, 倒序排列, 打印输出
System.out.println("3=================");
list.stream()
.filter(e -> e >= 60)
.sorted(( o1, o2) -> o1 > o2? 1 : -1) //自定义比较器排序,这里是定义为了降序排序
.forEach(System.out::println);
//需求4: 找出所有及格的分数, 倒序排列, 取前3名, 打印输出
System.out.println("4=================");
list.stream()
.filter(e -> e >= 60)
.sorted(( o1, o2) -> o1 > o2? 1 : -1)
.limit(3) //获取排序出之后的前3个元素
.forEach(System.out::println);
//需求5: 找出所有及格的分数, 倒序排列, 取前4-6名, 打印输出
System.out.println("5=================");
list.stream()
.filter(e -> e >= 60)
.sorted(( o1, o2) -> o1 > o2? 1 : -1)
.skip(3)
.limit(3)
.forEach(System.out::println);
//需求6: 找出所有及格的分数, 倒序排列, 取前4-6名, 将每个人的分数加10分, 打印输出
System.out.println("6=================");
list.stream()
.filter(e -> e >= 60)
.sorted(( o1, o2) -> o1 > o2? 1 : -1)
.skip(3)
.limit(3)
.map(new Function<Integer, Object>() {
@Override
public Object apply(Integer e) {
return e + 10;
}
})
.forEach(System.out::println);
}
}
Stream常见的终结方法
终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。
它的作用是对流中的数据进行筛选(遍历、最大、最小、统计个数)
//1. 打印出集合中所有元素
list.stream().forEach((e)->{
System.out.println(e);
});
//2. 统计出 身高不足170的 人数
long count = list.stream()
.filter(student -> student.getHeight() < 170)
.count();
System.out.println("count = " + count);
收集方式的终结方法