集合操作是编程中使用频率非常高的,所有有一款针对集合的操作工具是非常有必要的。通过框架提供的工具一方面可以减少开发相似功能的耗时;同时框架在安全与稳定性上更被推荐。
Guava Collect是Guava工具包中的一个子模块,主要对jdk中的集合操作添加了一些简易的API,同时也是对Collections工具类的扩展。当然Guava还定义了一些特定场景的数据结构以及一些针对jdk集合的优化,最典型的就是Immutable Collections(不可变集合),你会发现调用Guava API很多都是不可变的
我们常见的集合类有:
集合是一种非常常见的数据结构,JDK在处理各种数据集时,提供了以上集合类型的数据结构以及其对应API方便开发者高效简易地对数据对象操作
guava主要提供了以下几个方面的支持:
Guava Collect作为集合操作工具,我们主要从实际业务中了解其能够帮助我们实现怎样的需求,下面看下其API的使用情况:
假设我们有10000名学生,通过Faker生成这些模拟的学生数据数据:
List students = new ArrayList<>();
Faker faker = new Faker(Locale.CHINA);
@Before
public void init(){
Faker enFaker = new Faker();
Name name = faker.name();
IntStream.range(0,10000).forEach(index->{
students.add(
Student.of()
.setId(String.valueOf(index+1))
.setName(name.name())
.setAge(faker.number().numberBetween(18,22))
.setGender(new String[]{"男","女"}[faker.number().numberBetween(0,2)])
.setAddress(faker.address().streetAddress())
.setScore(faker.number().randomDouble(3,50,100))
.setEmail( faker.internet().emailAddress(enFaker.name().username()))
.setTelephone(faker.phoneNumber().cellPhone())
);
});
}
Multiset
获取元素出现频次。比如获取男生与女生的学生数量分别为多少
@Test
public void multiset(){
Multiset multiset = HashMultiset.create();
students.forEach(student -> {
if(Objects.equals(student.getGender(),"男")){
multiset.add("男");
}else{
multiset.add("女");
}
});
System.out.println("学生中男生数量:"+ multiset.count("男"));
System.out.println("学生中女生数量:"+ multiset.count("女"));
}
Multimap
一个键对应多个值时。比如查看各个年龄的学生是哪些
@Test
public void multimap(){
ListMultimap multimap =
MultimapBuilder.hashKeys().arrayListValues().build();
students.forEach(student -> {
multimap.put(student.getAge(),student);
});
System.out.println( multimap.get(20) );
}
BiMap
键和值都是唯一时。比如处理学生的邮箱和手机号,客户互换键值位置
@Test
public void biMap(){
BiMap biMap = HashBiMap.create();
students.forEach(student -> {
biMap.put(student.getEmail(),student.getTelephone());
});
BiMap inverse = biMap.inverse();// 键值更换
System.out.println( biMap );
System.out.println( inverse );
}
Table
二维表,通过行(键)、列(键)取值 比如可以以学生为行数据,其中id为行键,列名分别为学生属性名称
ID姓名年龄性别1TOM22男
@Test
public void table(){
Table weightedGraph = HashBasedTable.create();
students.forEach(student -> {
weightedGraph.put(student.getId(), "姓名", student.getName());
weightedGraph.put(student.getId(), "年龄", student.getAge());
weightedGraph.put(student.getId(), "性别", student.getGender());
weightedGraph.put(student.getId(), "邮箱", student.getEmail());
weightedGraph.put(student.getId(), "电话", student.getTelephone());
weightedGraph.put(student.getId(), "地址", student.getAddress());
weightedGraph.put(student.getId(), "分数", student.getScore());
});
Map row = weightedGraph.row("1");
Map column = weightedGraph.column("姓名");
Set> cells = weightedGraph.cellSet();
System.out.println( row );
System.out.println( column );
System.out.println( cells );
}
ClassToInstanceMap
当值是键的类型实例时,通过该Map现在键值关系
@Test
public void classToInstanceMap(){
ClassToInstanceMap numberDefaults = MutableClassToInstanceMap.create();
numberDefaults.put(Number.class,1);
Map objectMap = new HashMap<>();
objectMap.put(Number.class,2);
}
RangeSet
区间Set。比如通过学生分数确定学生等级
@Test
public void rangeSet(){
RangeSet ArangeSet = TreeRangeSet.create();
ArangeSet.add(Range.closed(90d,100d)); // [90,100]
RangeSet BrangeSet = TreeRangeSet.create();
BrangeSet.add(Range.closedOpen(80d,90d)); // [80,90)
RangeSet CrangeSet = TreeRangeSet.create();
CrangeSet.add(Range.closedOpen(70d,80d)); // [70,80)
RangeSet DrangeSet = TreeRangeSet.create();
DrangeSet.add(Range.closedOpen(60d,70d)); // [60,70)
RangeSet ErangeSet = TreeRangeSet.create();
ErangeSet.add(Range.lessThan(60d)); // [...,60)
students.forEach(student -> {
System.out.print( " 学生:"+ student.getName() );
System.out.print( ",分数为:"+ student.getScore() );
String rank = "";
if(ArangeSet.contains(student.getScore())){
rank = "A";
}else if(BrangeSet.contains(student.getScore())){
rank = "B";
}else if(CrangeSet.contains(student.getScore())){
rank = "C";
}else if(DrangeSet.contains(student.getScore())){
rank = "D";
}else if(ErangeSet.contains(student.getScore())){
rank = "E";
}
System.out.print( ",等级为:"+ rank +"
");
});
}
RangeMap和RangeSet类似,区别是添加了区间命名。和上面一样
@Test
public void rangeMap(){
RangeMap rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(90d,100d),"A"); // [90,100]
rangeMap.put(Range.closedOpen(80d,90d),"B"); // [80,90)
rangeMap.put(Range.closedOpen(70d,80d),"C"); // [70,80)
rangeMap.put(Range.closedOpen(60d,70d),"D"); // [60,70)
rangeMap.put(Range.lessThan(60d),"E"); // [...,60)
students.forEach(student -> {
System.out.print( " 学生:"+ student.getName() );
System.out.print( ",分数为:"+ student.getScore() );
System.out.print( ",等级为:"+ rangeMap.get(student.getScore()) +"
");
});
}
当然我们首先需要将数据使用Guava Collect对应的数据结构来存储数据,这样才能使用其对应的API:
自从Jdk中引入了集合Stream的操作后,从很大程度上简化了对集合的操作,以前大量代码现在可能简单几行就能够达到相同的效果,同时支持并发处理,一并提升了效率。
下面看下常见的集合基于stream操作,同样以上面的学生为例:
遍历 forEach
@Test
public void forEach(){
students.stream().forEach(System.out::println);
}
转换 map
将元素转换成其他类型。比如根据学生名称、性别组成新的List;以id为键元素为值的Map或者学生姓名拼接的字符串等等
@Test
public void transform(){
// 转换为数组
List listResult = students.stream()
.map((val)-> val.getName() + ":" + val.getGender()).collect(Collectors.toList());
System.out.println( listResult );
// 转换成String
String stringResult = students.stream().map(Student::getName).collect(Collectors.joining());
System.out.println( stringResult );
// 转换成Map
Map mapResult = students.stream().collect(
// key ,value ,mergerOperation, initialization
Collectors.toMap(Student::getName,Student::self,(v1,v2)->{
// 出现相同key时的合并规则
return null;
},HashMap::new)
);
System.out.println( mapResult );
}
过滤 filter
根据条件匹配满足要求的元素。如找出分数大于80分的学生
@Test
public void filter(){
List filterResult = students.stream().filter((val)->{
return val.getScore()>80;
}).collect(Collectors.toList());
System.out.println(filterResult);
}
拆解 flatMap
将二层级集合进行拆解,并成一级集合。如[[1,2,3],[4,5,6]] -> [1,2,3,4,5,6]
@Test
public void flatMap(){
//复合拆解
List result = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6))
.flatMap(subList -> subList.stream())
.collect(Collectors.toList());
System.out.println(result);// 1 2 3 4 5 6
}
计算实现数据的汇总、求平均值、最大值...,当然主要针对数字(Number)类型
@Test
public void calculate(){
// 求和
double sum = students.stream().mapToDouble(Student::getScore).sum();
// 最大值
double max = students.stream().mapToDouble(Student::getScore).max().getAsDouble();
// 最小值
double min = students.stream().mapToDouble(Student::getScore).min().getAsDouble();
// 平均值
double avg = students.stream().mapToDouble(Student::getScore).average().getAsDouble();
// 归约运算 fold . count、sum、min、max、average
DoubleSummaryStatistics doubleSummaryStatistics = students.stream().mapToDouble(Student::getScore).summaryStatistics();
}
归纳计算 reduce
在很多语言中都存在的函数,如python、javascript。数据的累加、map的功能
@Test
public void reduce(){
// 结果和identity(初始值)类型相同
// identity accumulator combiner
Map result = students.stream().reduce(
new HashMap(), //初始值
(map, student) -> {
map.put(student.getId(),student);
return map;
},
(map1, map2) -> {
// 并发执行时的map合并
return null;
}
);
}
并发 parallel
上面的操作我们还可以使用parallel对stream并发处理
Arrays.asList().stream().parallel()...;
Arrays.asList().parallelStream()...;
分段处理对集合按固定规格分段处理,处理大批量数据时,结合parallel实现分段并发处理来提示效率
@Test
public void partition(){
List list = new ArrayList<>();
int partition = 100; //每段100个元素
int part = list.size() / partition + (list.size() % partition==0? 0:1);
Stream.iterate(0, n -> n+1)
.limit(part)
.parallel() //并发
.map(index -> list.stream().skip(index * partition).limit(partition).parallel().collect(Collectors.toList()))
.forEach(System.out::println);
}
本章主要介绍了Guava Collect部分,以及对集合操作的常用API,通过示例可以看到有其对JDK集合的扩展有了更广泛与简易的操作。同时在JDK引入 了Stream操作后,Guava Collect中的很多功能通过Stream也可以比较容易的实现了,当然具体如何选择根据实际情况。需要注意的是Guava Collect中 返回的基本都是不可变的集合,这样在对数据的操作会更加的安全。
页面更新:2024-03-25
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号