如何过滤具有布尔变量定义的 3 种过滤器类型的 ArrayList?



我有主要ArrayList<Car> cars,我试图以三种不同的方式过滤。每个过滤器可以单独使用,也可以一起使用。

因此,如果有三个过滤器:x、y 和 z,并且只有 x 和 y 为真,那么一个新的数组列表将考虑过滤器 x 和 y,并且将显示每个条目。如果 x、y 和 z 都为真,则主数组列表 'cars' 将被所有三种方法过滤,并且将创建一个新的数组列表,然后显示所有过滤后剩下的内容。

我为每个过滤器都有布尔实例变量,每当调用特定过滤器的方法时,这些变量都设置为 true。例如,如果我要过滤cars数组列表以查看汽车是否电动,我会在public void filterByElectric()方法的开头设置filterElectric = true,然后添加代码,其中我使用 for 循环来检查数组列表的每个索引位置中的每个Car对象是否电动。

我知道我可以简单地做一个大的 if 语句,这将允许我说if (filterElectric == true)然后显示带有过滤汽车的新数组列表,我知道我可以使用&&一次使用多个过滤器(如果它们是真的),只需将新创建的数组列表附加到一起并显示每个新的过滤条目和一个 for 循环, 但这样做意味着我必须为所有场景创造条件。

我如何能够首先检查三个过滤器中的哪些过滤器是真的,然后在我知道哪些过滤器变量设置为 true 后,我可以将这两个过滤列表附加到一起以显示?


public void displayEverything() {
if (filX == true && filY == true && filZ == true) {
// append all filtered lists and then print newly created
// list to system
filtered.addAll(filX);
filtered.addAll(filY);
filtered.addAll(filZ);
for (Car a : filtered) {
System.out.println(a.disp());
}
} else if (filX == true && filY == true & filZ == false) {
filtered.addAll(filX);
filtered.addAll(filY);
for (Car a : filtered) {
System.out.println(a.disp());
}
// then it keeps going.... 
// is there a faster way to do this 
// instead of writing all conditions with &&'s?
}
}

public void filterX() {
filX = true;
for (Car a : cars) {
if (a.getWhatever() == 1) {
xList.add(a);
} else {
break;
}
}
}
public void filterY() {
filY = true;
for (Car a : cars) {
if (a.getSomething() == true) {
yList.add(a);
} else {
break;
}
}
}
public void filZ(double a, double b) {
filZ = true;
for (Car a : cars) {
if (a.getP() >= a && a.getP() <= b) {
zList.add(a);
} else {
break;
}
}
}

如果你希望你的代码尽可能短,你可以这样做:

public static List<Car> filter(
List<Car> allCars,
boolean onlyElectric,
boolean onlyAllWheelsDrive,
boolean onlyAutomatic)
{
return allCars.stream()
.filter((Car car) -> !onlyElectric || car.isElectric())
.filter((Car car) -> !onlyAllWheelsDrive|| car.isAllWheelsDrive())
.filter((Car car) -> !onlyAutomatic|| car.isAutomatic())
.collect(Collectors.toList());
}

!onlyElectric || …乍一看有点奇怪。它可以用另一种方式编写:

.filter((Car car) -> onlyElectric ? car.isElectric() : true)

这可以更容易地翻译成人类可以理解的语言:如果选择了"onlyElectric"过滤器,请检查汽车是否真的是电动的。如果未选择,只需返回 true,这意味着过滤器接受每辆车。

在你写这段代码的那一刻,它就有意义了。几天后,当您回来添加另一个过滤器时,您可能会问自己:3 个布尔参数中的哪一个是哪个,为什么它们完全按照这个顺序排列?另一种解决方案是使用enum而不是这些布尔值。

public enum CarFilter {
Electric, AllWheelsDrive, Automatic
}
public static List<Car> filter(
List<Car> allCars,
CarFilter... filters)
{
EnumSet<CarFilter> set = EnumSet.noneOf(CarFilter.class);
Collections.addAll(set, filters);
return allCars.stream()
.filter((Car car) -> !set.contains(CarFilter.Electric) || car.isElectric())
.filter((Car car) -> !set.contains(CarFilter.AllWheelsDrive) || car.isAllWheelsDrive())
.filter((Car car) -> !set.contains(CarFilter.Automatic) || car.isAutomatic())
.collect(Collectors.toList());
}

这样,您可以像这样调用该方法:

filter(allCars, CarFilter.Electric, CarFilter.Automatic);

此代码肯定包含比以下更多的信息:

filter(allCars, true, false, true);

快速解决方案,这可能是您正在寻找的。

从汽车开始:

package so.car;
public class Car {
private String name;
private boolean electric;
private boolean awd;
private boolean automatic;
public Car(String name, boolean electric, boolean awd, boolean automatic) {
this.name = name;
this.electric = electric;
this.awd = awd;
this.automatic = automatic;
}
public boolean isElectric() {
return electric;
}
public void setElectric(boolean electric) {
this.electric = electric;
}
public boolean isAwd() {
return awd;
}
public void setAwd(boolean awd) {
this.awd = awd;
}
public boolean isAutomatic() {
return automatic;
}
public void setAutomatic(boolean automatic) {
this.automatic = automatic;
}
@Override
public String toString() {
return "Car [name=" + name + "]";
}
}

然后处理一次运行多个过滤器:

package so.car;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
public class CarFilters {
private Map<String, Function<Car, Boolean>> filters;
public CarFilters(Map<String, Function<Car, Boolean>> filters) {
this.filters = filters;
}
public List<String> getPassingFilterNames(Car car) {
List<String> passingFilterNames = new ArrayList<>();
for (Entry<String, Function<Car, Boolean>> entry : filters.entrySet()) {
String filterName = entry.getKey();
Function<Car, Boolean> predicateFunction = entry.getValue();
if (predicateFunction.apply(car)) {
passingFilterNames.add(filterName);
}
}
return passingFilterNames;
}
}

最后是一个简单的驱动程序类: 包装所以汽车;

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
Map<String, Function<Car, Boolean>> filterNameMap = new HashMap<>();
filterNameMap.put("Electric", car -> car.isElectric());
filterNameMap.put("All Wheel Drive", car -> car.isAwd());
filterNameMap.put("Automatic Transmission", car -> car.isAutomatic());
CarFilters carFilters = new CarFilters(filterNameMap);
Car _0 = new Car("zero", true, true, true);
Car _1 = new Car("one", true, false, true);
Car _2 = new Car("two", false, false, true);
Car _3 = new Car("three", true, true, true);
List<Car> cars = new ArrayList<>();
cars.add(_0);
cars.add(_1);
cars.add(_2);
cars.add(_3);
Map<List<String>, List<Car>> filtersAndCars = cars.stream()
.map(car -> {
List<String> filters = carFilters.getPassingFilterNames(car);
return new SimpleEntry<>(filters, new ArrayList<>(Arrays.asList(car)));
})
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (l1, l2) -> {
l1.addAll(l2);
return l1;
}));
System.out.println(filtersAndCars);
}
}

这将产生:

[Automatic Transmission]=[Car [name=two]],
[Automatic Transmission, Electric]=[Car [name=one]]
[All Wheel Drive, Automatic Transmission, Electric]=[Car [name=zero], Car [name=three]]

您可以根据要应用的过滤逻辑构建谓词,只需使用此谓词来过滤流:

public Predicate<Car> filter = car ->  {
boolean filter = true;
if (isfilterEletricOn()) {
filter = filter && car.isEletric();
}
if (isfilterOffRoadOn()) {
filter = filter && car.isOffRoad();
}
if (isfilterSportOn()) {
filter = filter && car.isSport();
}
return filter;
};
cars.stream().filter(filter).collect(Collectors.toList());

你可以做这样的事情:

public static Predicate<Car> filteringAll(boolean filterToyota, boolean filterAudi) {
return p -> (p.getManufacturer().getName().equals("Toyota") && filterToyota) || (p.getManufacturer().getName().equals("Audi") && filterAudi);
}

并在代码中使用它,如下所示:

List<Car> listOfFilteredCars = listOfCars.stream().filter(filteringAll(false, true)).collect(Collectors.toList());

因此,如果您想过滤奥迪和丰田,您可以将它们的过滤器都设置为 true,如果您只想过滤丰田汽车,则仅将丰田过滤器设置为 true,将奥迪过滤器设置为 false。

完整运行代码:

主类

public class Main {
List<Car> listOfCars = new ArrayList<>();
public static void main(String[] args) {
List<Car> listOfCars = new ArrayList<>();
listOfCars.add(new Car(Manufacturer.TOYOTA, "Toyota A", 90));
listOfCars.add(new Car(Manufacturer.TOYOTA, "Toyota B", 35));
listOfCars.add(new Car(Manufacturer.AUDI, "Audi B", 64));
listOfCars.add(new Car(Manufacturer.MERCEDES, "Mercedes C", 464));
listOfCars.add(new Car(Manufacturer.TOYOTA, "Toyota C", 33));
listOfCars.add(new Car(Manufacturer.AUDI, "Audi A", 75));
listOfCars.add(new Car(Manufacturer.TOYOTA, "Toyota C", 23636));
List<Car> listOfFilteredCars = listOfCars.stream().filter(filteringAll(false, true, false)).collect(Collectors.toList());
System.out.println(listOfFilteredCars);
}
public static Predicate<Car> filteringAll(boolean filterToyota, boolean filterAudi, boolean filterMercedes) {
return p -> (p.getManufacturer().equals(Manufacturer.TOYOTA) && filterToyota) || (p.getManufacturer().equals(Manufacturer.AUDI) && filterAudi) || (p.getManufacturer().equals(Manufacturer.MERCEDES) && filterMercedes) ;
}
}

汽车类

public class Car {
private Manufacturer manufacturer;
private String nameOfTheCar;
private double price;
public Car() {
}
public Car(Manufacturer manufacturer, String nameOfTheCar) {
this.manufacturer = manufacturer;
this.nameOfTheCar = nameOfTheCar;
}
public Car(Manufacturer manufacturer, String nameOfTheCar, double price) {
this.manufacturer = manufacturer;
this.nameOfTheCar = nameOfTheCar;
this.price = price;
}
public Manufacturer getManufacturer() {
return manufacturer;
}
public String getNameOfTheCar() {
return nameOfTheCar;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}

@Override
public String toString() {
return "Manufacturer: " + getManufacturer() + ", name: " + getNameOfTheCar();
}
}

制造商类

public enum Manufacturer {
AUDI, MERCEDES, TOYOTA;
}

对于更动态的解决方案,您可以创建一个具有active属性和filter谓词的MyFilter类:

public class MyFilter<T> {
private boolean isActive;
private Predicate<T> filter;
// constructor and/or getter and setter
}

现在,您可以创建(非)活动过滤器的动态列表:

List<MyFilter<MyObject>> filters = Arrays.asList(
new MyFilter<>(true, MyObject::isA),
new MyFilter<>(false, MyObject::isB),
new MyFilter<>(true, MyObject::isC)
);

最后,您可以过滤您的List<MyObject>

List<MyObject> result = objects.stream()
.filter(o -> filters.stream().filter(MyFilter::isActive).allMatch(f -> f.getFilter().test(o)))
.collect(Collectors.toList());

为了使整个事情更具可读性,您可以在过滤器中添加一个test方法:

public boolean test(T object) {
return !isActive || filter.test(object);
}

并使用以下方法过滤您的列表:

List<MyObject> result = objects.stream()
.filter(o -> filters.stream().allMatch(f -> f.test(o)))
.collect(Collectors.toList());

如果您只想匹配一个过滤器,您可以使用anyMatch而不是allMatch。要否定结果,您可以使用noneMatch

最新更新