引入
在上周周赛中,有这么一道题:
1333.餐厅过滤器
给你一个餐馆信息数组 restaurants,其中 restaurants[i] = [idi, ratingi, veganFriendlyi, pricei, distancei]。你必须使用以下三个过滤器来过滤这些餐馆信息。
其中素食者友好过滤器 veganFriendly 的值可以为 true 或者 false,如果为 true 就意味着你应该只包括 veganFriendlyi 为 true 的餐馆,为 false 则意味着可以包括任何餐馆。此外,我们还有最大价格 maxPrice 和最大距离 maxDistance 两个过滤器,它们分别考虑餐厅的价格因素和距离因素的最大值。
过滤后返回餐馆的 id,按照 rating 从高到低排序。如果 rating 相同,那么按 id 从高到低排序。简单起见, veganFriendlyi 和 veganFriendly 为 true 时取值为 1,为 false 时,取值为 0 。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200128144005907.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L05vX0dhbWVfTm9fTGlmZV8=,size_16,color_FFFFFF,t_70)
本题题目条件的确是特别的多,我们先来捋一捋:
首先查看题目给定的函数,其输入为:
public List<Integer> filterRestaurants(int[][] restaurants, int veganFriendly, int maxPrice, int maxDistance)
- restaurants的二维数组,每个餐厅包含了id、rating、veganFriendly、price、distance这五个元素,也就是每个restaurant[i]包含了长度为5的数组。
- 然后会根据三个值来过滤,分别是veganFriendly,这是个二元变量,由0或者1来表示;maxPrice也就是说price要小于这个值;maxDistance也就是会所distance要小于这个值。所以这三个输入给定了筛选的条件。
- 筛选出来的值排序,根据什么来排序呢?rating和id来排序,先比较rating然后比较id。
- 输出id。
总结到上面这一步,我们发现,本题实际上是十分简单的,就是进行filter->sort,所以,我们这里使用java8的特性流来解题,显得更加优雅。
本题题解
本题思路基本如下:
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
- 生成流
首先将输入转化为流的形式
+--------------------+
| stream of elements |
+--------------------+
一般是通过这样的方式转换为流:
List<Object> list=new ArrayList<>();
list.stream();
所以,我们需要创建一个对象来保存餐厅:
private class Restaurant {
int id;
int rating;
int veganFriendly;
int price;
int distance;
public Restaurant(int id, int rating, int veganFriendly, int price, int distance) {
this.id = id;
this.rating = rating;
this.veganFriendly = veganFriendly;
this.price = price;
this.distance = distance;
}
}
然后创建这个List:
List<Restaurant> restaurantList = new ArrayList<>();
for (int[] restaurant : restaurants) {
restaurantList.add(new Restaurant(restaurant[0], restaurant[1],
restaurant[2], restaurant[3], restaurant[4]));
}
于是这个stream就出来了:
restaurantList.stream();
- 过滤filter
+------+
|filter|
+------+
过滤其实就是根据过滤条件来过滤了,一般是通过下面的方法:
list.stream().filter(o->o.value>5);//过滤一个出布尔值
本题的过滤条件已经讲的很清楚了,会根据三个值来过滤,分别是veganFriendly,这是个二元变量,由0或者1来表示;maxPrice也就是说price要小于这个值;maxDistance也就是会所distance要小于这个值。
所以其代码为:
restaurantList.stream().filter(r->r.veganFriendly >= veganFriendly
&&r.price<=maxPrice
&&r.distance<maxDistance);
- 排序
+------+
|sorted|
+------+
一般排序的代码如下:
//以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序
Random random = new Random();
random.ints().limit(10).sorted();
上面的代码是对随机数(int类型)从小到大排序,我们这里传入的是一个对象Restaurant,所以不能简单的使用sorted()方法。
如果只是简单的按照rating排序,可以这么写:
restaurantList.stream().sorted(Comparator.comparing(Restaurant::getRating));
或者:
restaurantList.stream().sorted((o1,o2)->{
return o2.rating-o1.rating;
});
不过本题的要求更多一点:
过滤后返回餐馆的 id,按照 rating 从高到低排序。如果 rating 相同,那么按 id 从高到低排序。
所以我们按照第二种方式来写:
restaurantList.stream().sorted((o1, o2) -> {
if (o1.rating == o2.rating) {
return o2.id - o1.id;
}
return o2.rating - o1.rating;
})
- 返回id
+---+
|map|
+---+
由于我们只需要返回id,所以需要采用map,一般map是这样用的:
// map 输出了元素对应的平方数
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i);
所以在这里,我只需要:
restaurantList.stream().map(r->r.id);
- 返回List
+-------+
|collect|
+-------+
本题要求返回的是List<Integer>,所以我们只需要这样处理:
restaurantList.stream().map(r->r.id).collect(Collectors.toList());
完整代码:
class Solution {
public List<Integer> filterRestaurants(int[][] restaurants, int veganFriendly, int maxPrice, int maxDistance) {
List<Restaurant> restaurantList = new ArrayList<>();
for (int[] restaurant : restaurants) {
restaurantList.add(new Restaurant(restaurant[0], restaurant[1],
restaurant[2], restaurant[3], restaurant[4]));
}
return restaurantList.stream().filter(r -> r.veganFriendly >= veganFriendly
&& r.price <= maxPrice && r.distance <= maxDistance).sorted((o1, o2) -> {
if (o1.rating == o2.rating) {
return o2.id - o1.id;
}
return o2.rating - o1.rating;
}).map(r -> r.id).collect(Collectors.toList());
}
private class Restaurant {
int id;
int rating;
int veganFriendly;
int price;
int distance;
public Restaurant(int id, int rating, int veganFriendly, int price, int distance) {
this.id = id;
this.rating = rating;
this.veganFriendly = veganFriendly;
this.price = price;
this.distance = distance;
}
}
}
后记:int[]、Integer[]、List的互相转化
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
int[] data = {4, 5, 3, 6, 2, 5, 1};
// int[] 转 List<Integer>
List<Integer> list1 = Arrays.stream(data).boxed().collect(Collectors.toList());
// Arrays.stream(arr) 可以替换成IntStream.of(arr)。
// 1.使用Arrays.stream将int[]转换成IntStream。
// 2.使用IntStream中的boxed()装箱。将IntStream转换成Stream<Integer>。
// 3.使用Stream的collect(),将Stream<T>转换成List<T>,因此正是List<Integer>。
// int[] 转 Integer[]
Integer[] integers1 = Arrays.stream(data).boxed().toArray(Integer[]::new);
// 前两步同上,此时是Stream<Integer>。
// 然后使用Stream的toArray,传入IntFunction<A[]> generator。
// 这样就可以返回Integer数组。
// 不然默认是Object[]。
// List<Integer> 转 Integer[]
Integer[] integers2 = list1.toArray(new Integer[0]);
// 调用toArray。传入参数T[] a。这种用法是目前推荐的。
// List<String>转String[]也同理。
// List<Integer> 转 int[]
int[] arr1 = list1.stream().mapToInt(Integer::valueOf).toArray();
// 想要转换成int[]类型,就得先转成IntStream。
// 这里就通过mapToInt()把Stream<Integer>调用Integer::valueOf来转成IntStream
// 而IntStream中默认toArray()转成int[]。
// Integer[] 转 int[]
int[] arr2 = Arrays.stream(integers1).mapToInt(Integer::valueOf).toArray();
// 思路同上。先将Integer[]转成Stream<Integer>,再转成IntStream。
// Integer[] 转 List<Integer>
List<Integer> list2 = Arrays.asList(integers1);
// 最简单的方式。String[]转List<String>也同理。
// 同理
String[] strings1 = {"a", "b", "c"};
// String[] 转 List<String>
List<String> list3 = Arrays.asList(strings1);
// List<String> 转 String[]
String[] strings2 = list3.toArray(new String[0]);
}
}