学习目标
本文知识是对集合层级的介绍,应用开发中实际使用的是他们的子级,感兴趣的小伙伴或者想深入了解有关Java集合知识的朋友可以选择阅读!
Stream的方法使用使用部分代码块内大多有两种实现方式,是为了更好的理解方法底层的代码,不必过于深究,可以看懂链式应用的方法即可
- 核心掌握集合层级
- 了解Collection
- 了解Collections
- 了解Stream
1.集合概念
● 集合和数组一样也是容器,但是只能放对象
● 集合和数组相比,数组长度是固定的(一旦确定之后不能改动) 集合长度不限
● 数组中数组元素要求数据类型一致;集合中可以放任意的对象(只能是对象 ,只不过对象类型不要求一致)
● 集合中不能放基本数据类型,只能放对象
● 集合中有比较多的数据结构 可供选择
2.集合体系
3.集合分类
● 有2大类的集合: 存储的数据特征。
● 1、Collection接口: 数据只能是单值(一个个的数据) 每次存储一个元素/数据
● 问题: 元素是否可重复? 元素是否有序(索引位置)?
List: 元素有序可重复
Set: 元素无序且唯一
● 2、Map<K,V> 接口 : 数据必须是一组元素(2个数据) 有key–value
● key: 必须唯一
● value: 可以重复
4.Collection<T>
● java.util.Collection
● 表示集合层次的根接口。
● 存储单值元素。
4.1 层级
public interface Collection<E> extends Iterable<E>{}
4.2 常用方法
4.3 使用方法
- 无泛型
public static void collectionMethod() {
Collection arrayList = new ArrayList();
Collection arrayList2 = new ArrayList();
arrayList2.add(66);
arrayList2.add(99);
// 自动装箱 1 int-->Integer
arrayList.add(10);
String a = "abc";
arrayList.add(99);
System.out.println("添加是否成功:" + arrayList.add(a));
System.out.println("集合是否为空,没有集合元素:" + arrayList.isEmpty());
// Integer是不可变的 ,一旦改值 那就是新的对象
System.out.println("移除Integer是否成功:" + arrayList.remove(2));
// remove进行移除的时候,看集合中是否包含这个对象 如果有那么移除成功
// System.out.println("移除stringBuilder是否成功:" + arrayList.remove(stringBuilder));
System.out.println("集合中的元素个数:" + arrayList.size());
String b = "abc";
System.out.println("集合中是否包含某个元素:" + arrayList.contains(b));
// 一次性 添加多个元素进来
//arrayList.addAll(arrayList2);
System.out.println("addAll:" + arrayList);
// arrayList.removeAll(arrayList2);// 只要元素和arrayList2相等 就删除
System.out.println("removeAll:" + arrayList); //
System.out.println("containsAll 全部包含: " + arrayList.containsAll(arrayList2));
//移除所有元素
arrayList.clear();
}
- 有泛型
private static void demo1() {
//1.创建集合对象
Collection collection = new ArrayList();
//2.操作
//2.1 新增
collection.add("hello");
collection.add("hello");
collection.add(100);
collection.add(true);
collection.add(10.123);
collection.add(null);
collection.add(new UserInfo("张三"));
//2.2 查看集合元素个数
System.out.println(collection.size());
//2.3 删除
collection.remove("hello");
collection.remove("hello");
/* collection.removeIf(new Predicate() {
@Override
public boolean test(Object obj) {
return "hello".equals(obj);
}
});*/
//collection.removeIf("hello"::equals);
//collection.clear();
//2.4判断
System.out.println(collection.contains(1000));
System.out.println(collection.isEmpty());
System.out.println(collection.size()==0);
System.out.println(collection.size());
System.out.println(collection.toString());//
}
4.4 遍历集合
- 集合迭代器常用方法
private static void demo2() {
Collection<Integer> collection = new ArrayList<>();
//java.util.Collections工具类 操作集合元素静态方法 Arrays
Collections.addAll(collection, 10, 20, 30, 1, 5);
//遍历集合元素的方式: 其实就1种遍历集合的方式(迭代器)
// 2. 增强for
// 增强for遍历数组: class文件里面还是普通for循环
// 增强for遍历集合: class文件里面使用的iterator遍历集合元素的
for (Integer num : collection) {
System.out.println("num:"+num);
}
// 3. Iterator<E> iterator() 获得集合的迭代器对象
//3.1 获得集合的迭代器对象
Iterator<Integer> iterator = collection.iterator();//集合的数据都在iterator里面了
//3.2 判断光标之后是否有数据
while (iterator.hasNext()){
//3.3 获得光标之后的数据
Integer num = iterator.next();
System.out.println("num:"+num);
}
// 4. default void forEach(Consumer<? super T> action) 遍历集合元素 jdk1.8+
/*collection.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer num) {
System.out.println(num);
}
});*/
collection.forEach(System.out::println);
System.out.println(collection);
}
4.5 集合转数组
private static void demo3() {
Collection<Integer> collection = new ArrayList<>();
collection.add(100);//装箱
collection.add(200);
collection.add(300);
//集合转数组----> 数组操作比较复杂
//3. default <T> T[] toArray(IntFunction<T[]> generator);
//第3个方法 与第2个方法的思想一致的。 length=0
Integer[] integers = collection.toArray(new IntFunction<Integer[]>() {
@Override
public Integer[] apply(int value) {//0
System.out.println("value:" + value);
return new Integer[value];
}
});
Integer[] integers = collection.toArray(Integer[]::new);
System.out.println("integers:"+Arrays.toString(integers));
//2. <T> T[] toArray(T[] a); 建议使用 核心:数组
Integer[] numArray = new Integer[0];
//2.1 length==collection.size() 保证数组里面存储size个元素
//2.2 length>collection.size() 问题: 数组的空间比较大的 null----> 功能实现 有可能会出现NPE
//2.3 length<collection.size() 可以转换成功
//站在并发/性能/扩容: 建议指定的数组的length=0
Integer[] toArray = collection.toArray(numArray);
System.out.println("numArray:" + Arrays.toString(numArray));
System.out.println("toArray:" + Arrays.toString(toArray));
System.out.println(numArray == toArray);
Object[] array = collection.toArray();
//1. 不要使用Object[] toArray() 不管集合里面存储什么类型的数据 都要转成一个Object[]
for (Object obj : array) {
Integer num = (Integer) obj;
System.out.println(num + 100);
}
System.out.println(Arrays.toString(array));
}
4.6 匿名内部类
● 在任意一个类和接口都能创建匿名内部类,相当于定义了一个 没有名字的新的类。
● 等价于创建了一个类的子类,以及接口的实现类。
- 创建接口
public interface MyInterface {
void method1();
}
- 创建匿名内部类
public static void main(String[] args) {
// 匿名内部类 抽象类 和接口都能使用
test(new MyInterface() {
@Override
public void method1() {
System.out.println("method111===");
}
});//等价于接口的实类对象
}
public static void test(MyInterface myInter) {
myInter.method1();
}
public static void test1(){
Object obj = new Object(){
//等价于类的匿名内部类---->类的子类
}
}
提醒(可以先看这部分)
● 虽然我们讲解了集合的根接口Collection以及相关的方法。
● 但是在开发中一般不用父接口,用的还是子级,List或者Set
● 我们只需要知道Collection里面维护的都是List以及Set集合都有的方法即可。
5.Collections的使用
● 工具类。在java.util包。
● 类似于之前学的Arrays。
● Collections操作集合(Collection->List/Set)元素的工具类
- 常用方法
private static void demo1() {
//1.Collections.addAll(Collection,T...data); 将多个数据新增到指定的集合对象中
List<Integer> list = new ArrayList<>(10);
//list.add(10);
Collections.addAll(list, 10, 10, 1, 3, 4, 50, 24, 100);
Set<Integer> set = new HashSet<>(16);
Collections.addAll(set, 10, 10, 1, 3, 4, 50, 24, 100);
List<Integer> list1 = List.of(1, 2, 3);//只能读数据
Set<Integer> set1 = Set.of(1, 2, 3, 4, 5);
Map<Integer, Integer> map = Map.of(1, 1, 2, 2, 3, 3, 4, 4);
//数组转集合
List<Integer> list2 = Arrays.asList(1, 2, 3);
}
- 常用的静态常量
private static List<?> demo3() {
//获得一个空集合对象 等价于size=0
System.out.println(Collections.EMPTY_LIST);//[]
System.out.println(Collections.EMPTY_MAP);//{}
System.out.println(Collections.EMPTY_SET);//[]
System.out.println(Collections.emptySet());
System.out.println(Collections.emptyMap());
System.out.println(Collections.emptyList());
//前提: 方法的返回值是特定的集合类型
//在功能里面 可以作为返回值使用 判断的时候避免出现NPE
return Collections.EMPTY_LIST;
}
private static void demo2() {
//2.将线程不安全的集合对象转换成线程安全的集合对象 可以在并发的环境下 作为全局变量使用
Collections.synchronizedList(new ArraysList()); Vector
Collections.synchronizedSet(new HashSet())
Collections.synchronizedMap(new HashMap()) ConcurrentHashMap
//以下2个方法 只针对于TreeMap与TreeSet
Collections.synchronizedSortedMap(new TreeMap())
Collections.synchronizedSortedSet(new TreeSet())
}
- sort
private static void demo4() {
//Collections.sort(List); 要求集合元素类型必须实现Comparable
//Collections.sort(List,Comparator);
//如果真的是对list集合排序,一般我们只会使用List.sort()
List<Integer> list = new ArrayList<>(10);
Collections.addAll(list, 10, 10, 1, 3, 4, 50, 24, 100);
Collections.sort(list);
System.out.println(list);
System.out.println("-----------------------");
List<UserInfo> userInfoList = new ArrayList<>(10);
Collections.addAll(userInfoList,
new UserInfo(1, "jim", 20),
new UserInfo(2, "tom", 18),
new UserInfo(3, "lilei", 23),
new UserInfo(4, "hanmeimei", 16)
);
//Collections.sort(userInfoList, Comparator.comparing(UserInfo::getAge));
userInfoList.sort(Comparator.comparing(UserInfo::getAge).reversed());
userInfoList.forEach(System.out::println);
}
- 获得集合的最值
private static void demo5() {
TreeSet<Integer> set = new TreeSet<>();
Collections.addAll(set, 10, 10, 1, 3, 4, 50, 24, 100);
System.out.println(set);//[1, 50, 3, 4, 100, 24, 10]
//获得Set集合的最值
//1.TreeSet
System.out.println(set.first());
System.out.println(set.last());
System.out.println("------------------------------------");
Set<Integer> hashSet = new HashSet<>(16);
Collections.addAll(hashSet, 10, 10, 1, 3, 4, 50, 24, 100);
//Collections.sort(List); 只能对list集合排序 不能对set集合排序
//5. 最值
Collections.max(Collection); 集合元素类型必须实现Comparable
Collections.max(Collection,Comparator);
Collections.min(Collection);
Collections.min(Collection,Comparator);
Integer max = Collections.max(hashSet);
Integer min = Collections.min(hashSet);
System.out.println(max);
System.out.println(min);
}
- shuffle
private static void demo6() {
// Collections.shuffle(List); 随机(随机数)打乱集合元素顺序。 洗牌。
List<Integer> list = new ArrayList<>(10);
Collections.addAll(list, 10, 1, 3, 4, 50, 24);
System.out.println(list);
Collections.shuffle(list);
System.out.println(list);
}
6.Stream
● 丰富了对Collection(List,Set)的操作
● 可以看成是一个增强版的迭代器。数据源不限,可以是多个数据源(Collection集合对象)
● 中间操作 属于 惰性操作 只有碰到终止操作才会执行
○ 凡是返回值是Stream类型的都是中间操作
○ filter:对流中数据进行过滤:返回值为
true表示留下的数据
false表示过滤掉的
○ map: 把流中的每个数据经过相同的操作 映射为新的数据
○ sorted:对流中数据排序
○ limit :只保留指定个数的元素
○ peek:表示查看 流中元素
● 终止操作
○ 只要返回值不是Stream类型的
○ count:得到流中元素个数
○ reduce:合并 合并为一个数据
○ collect:流数据的收集
● Stream对象流是一次性的,只能被操作一次,不能重复操作
● 终止操作只能出现一次,在最后出现
6.1 获得Stream
private static void demo1() {
//获得Stream的方式
//1. 根据集合对象操作。Collection.stream()/paraStream()
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Stream<Integer> stream = list.stream();//串行化流 集合的数据都在Stream里面
Stream<Integer> stream1 = list.parallelStream();//并行化流
Iterator<Integer> iterator = list.iterator();
//2. 使用Stream接口里面的静态方法
Stream.concat(Stream,Stream);
Stream<Integer> stream2 = Stream.of(1, 2, 3);
//3.Arrays.stream()
//IntStream LongStream DoubleStream
//避免过多拆箱和装箱 提供了操作基本数据类型数据的Stream
IntStream stream2 = Arrays.stream(new int[]{1, 2, 3});
//4.Random.ints()
//随机数Random
//获得10个随机的数字。 0 200随机的数据
Random random = new Random();
int num = random.nextInt(200);
IntStream intStream = random.ints(10, 100, 200);
String str = "hello";
IntStream intStream1 = str.chars();
}
6.2 操作Stream
- Stream Stream.distinct() ; 去重
- Stream Stream.sorted() ; 排序
- Stream Stream.sorted(Comparator<? super T> comparator) ; 根据外部比较器排序规则排序
- Stream Stream skip(long n); 跳过数据源里面指定数量的数据
- Optional Stream.max(Comparator<? super T> comparator) ; 获得最值
- Optional Stream.min(Comparator<? super T> comparator) ;
- Stream.limit(long maxSize) ; 对数据源的数量限定
- Stream.forEach(Consumer<? super T> action) 循环遍历
- Stream.count(); 统计Stream里面的元素个数
- Stream.findAny() ; 查询任意一个数据
- Stream.findFirst() ;
- collect(Collector<? super T,A,R> collector) //收集操作之后的数据 存储到一个新的集合对象中
1.distinct/sort
private static void demo2() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 20, 2, 1, 3);
//对list集合元素进行去重/排序(降序)
//1.获得Stream对象
Stream<Integer> stream = list.parallelStream();
//2.操作Stream
Stream<Integer> stream1 = stream.distinct();
Stream<Integer> stream2 = stream1.sorted(Comparator.reverseOrder());
//3.获得操作之后满足条件的一些数据 存储到一个新的集合中---->收集起来 终止Stream
List<Integer> resultList = stream2.collect(Collectors.toList());
System.out.println(resultList);*/
//Stream就是一次性的。
//查询Stream里面还有多少个数据 终止Stream之后 无法再对Stream的数据进行相关的处理
//操作Stream: 一般都是方法链的方式
List<Integer> list1 = list.parallelStream().distinct().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println(list1);
}
2.allMatch/anyMatch
private static void demo3() {
List<String> list = List.of("hellom", "jiy", "toc");
//判断list集合的每个元素是否都包含指定的 m这个数据
for (int index = 0; index < list.size(); index++) {
String str = list.get(index);
if(str==null) break;
if(!str.contains("m")){
System.out.println(false);
return;
}
}
System.out.println(true);
System.out.println("----------------------------");
boolean flag = list.parallelStream().allMatch(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("m");
}
});
boolean flag = list.stream().anyMatch(str -> str.contains("m"));
System.out.println(flag);
}
3.filter
Stream filter(Predicate<? super T> predicate) ;//过滤Stream中不满足条件的数据
private static void demo4() {
List<String> list = List.of("zhangsan", "jim", "tom","lisi");
//获得list集合中: 过滤下来元素里面包含m的数据
List<String> nameList = new ArrayList<>(10);
for (String name : list) {
if(name.contains("m")){
nameList.add(name);
}
}
System.out.println("----------------------------");
List<String> stringList = list.parallelStream()
.filter(name -> name.contains("m")).collect(Collectors.toList());
System.out.println(stringList);
}
4.findFirst/findAny/min
private static void demo5() {
List<UserInfo> userInfoList = new ArrayList<>(10);
Collections.addAll(userInfoList,
new UserInfo(1, "jim", 20),
new UserInfo(2, "tom", 18),
new UserInfo(3, "lilei", 23),
new UserInfo(4, "hanmeimei", 16)
);
//获得年龄>=20的用户
List<UserInfo> collect = userInfoList.stream().filter(userInfo -> userInfo.getAge() >= 20).collect(Collectors.toList());
//使用lambda表达式
collect.forEach(System.out::println);
//获得userInfoList年龄最小的用户对象
//1.正常编写
userInfoList.sort(Comparator.comparing(UserInfo::getAge));
UserInfo userInfo = userInfoList.get(0);
System.out.println(userInfo);
System.out.println("-------------------");
userInfoList.forEach(System.out::println);
//或用Collections.min()
//2.Stream
Optional<UserInfo> first = userInfoList.stream().sorted(Comparator.comparing(UserInfo::getAge)).findFirst();
System.out.println(first);
System.out.println("-------------------");
userInfoList.forEach(System.out::println);
System.out.println("----------------------------");
UserInfo userInfo = userInfoList.parallelStream().findAny().orElse(null);
System.out.println(userInfo);
}
5.map/peek
场景: Stream里面的每个元素都要执行一样的功能的时候 。
//将stream里面的每个数据转换成另外一个类型的数据。
private static void demo6() {
List<UserInfo> userInfoList = new ArrayList<>(10);
Collections.addAll(userInfoList,
new UserInfo(1, "jim", 20),
new UserInfo(2, "tom", 18),
new UserInfo(3, "lilei", 23),
new UserInfo(4, "hanmeimei", 16)
);
//将集合里面每个用户的name的数据全部转换成大写字母
for (UserInfo userInfo : userInfoList) {
userInfo.setName(userInfo.getName().toUpperCase());
}
//获得集合里面每个用户对象的name的属性数据 全部转大写字母存储
List<String> collect = userInfoList.parallelStream().map(new Function<UserInfo, String>() {
@Override
public String apply(UserInfo userInfo) {
return userInfo.getName().toUpperCase();
}
}).collect(Collectors.toList());
List<String> collect = userInfoList.parallelStream()
.map(userInfo -> userInfo.getName().toUpperCase()).collect(Collectors.toList());
collect.forEach(System.out::println);
System.out.println("-----------------------------");
userInfoList.forEach(System.out::println);
}
private static void demo7() {
List<UserInfo> userInfoList = new ArrayList<>(10);
Collections.addAll(userInfoList,
new UserInfo(1, "jim", 20),
new UserInfo(2, "tom", 18),
new UserInfo(3, "lilei", 23),
new UserInfo(4, "hanmeimei", 16)
);
List<UserInfo> collect = userInfoList.stream().map(new Function<UserInfo, UserInfo>() {
@Override
public UserInfo apply(UserInfo userInfo) {
userInfo.setName(userInfo.getName().toUpperCase());
return userInfo;
}
}).collect(Collectors.toList());
/* List<UserInfo> collect = userInfoList.stream().map(userInfo -> {
userInfo.setName(userInfo.getName().toUpperCase());
return userInfo;
}).collect(Collectors.toList());*/
//前提: 对Stream的元素都执行同样的功能 最后集合里面的元素类型与原集合数据类型一致 建议使用peek替换map
List<UserInfo> collect = userInfoList.parallelStream()
.peek(userInfo -> userInfo.setName(userInfo.getName().toUpperCase())).collect(Collectors.toList());
System.out.println(collect);
System.out.println(userInfoList);
}
6.flatMap
private static void demo8() {
List<Integer> list1 = List.of(1, 2, 30, 4, 5);
List<Integer> list2 = List.of(10, 27, 3, 40, 5);
List<Integer> list3 = List.of(11, 2, 3, 4, 5);
List<Integer> list4 = List.of(100, 21, 3, 4, 50);
//获得前5个数据(降序)+去重
//核心: 将多个集合数据转换到一个集合中.
List<Integer> collect = Stream.of(list1, list2, list3, list4).flatMap(List::stream)
.distinct().sorted(Comparator.reverseOrder()).limit(5).collect(Collectors.toList());
System.out.println(collect);
/*Stream.of(list1, list2, list3, list4).flatMapToInt(new Function<List<Integer>, IntStream>() {
@Override
public IntStream apply(List<Integer> list) {
return list.stream().mapToInt(Integer::intValue);
}
})*/
Stream.of(list1, list2, list3, list4)
.flatMapToInt(list -> list.stream().mapToInt(Integer::intValue))
.distinct().sorted().limit(5).forEach(System.out::println);
}
7.reduce
private static void demo9() {
List<Integer> list = List.of(89, 90, 98);
int sum = 0;
for (Integer integer : list) {
sum+=integer;
}
Integer sum1 = list.stream().reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer num1, Integer num2) {
//System.out.println(num1+":"+num2);
return num1 + num2;
}
}).orElse(null);
Integer sum1 = list.stream().reduce(Integer::max).orElse(null);
System.out.println(sum1);
}