java 如何重构一个三维列表?

zte4gxcn  于 2023-06-20  发布在  Java
关注(0)|答案(4)|浏览(140)

我目前在我的Java项目中有一个三维列表。我用它来存储以下内容。我有一张巴黎的名单。一对有一个性别(男性,女性或混合)和一个食物偏好(肉类,蔬菜或素食)。
在我的代码中,我有时只能访问以蔬菜为食物偏好的配对,并且是男性或其他组合。我首先想到的是一个三维列表将是最好的。我的3D列表如下所示:

ArrayList<ArrayList<ArrayList<Pair>>> pairsSplitUp;

列表中填充了值,因此如果我们访问它,我们会得到以下内容:

  • 索引0:索引0:所有对是男性和有肉作为食物偏好
  • 索引0:索引1:所有对是女性和有肉作为食物偏好
  • 索引0:索引2:所有混合并以肉类为食物的配对
  • 索引1:索引0:所有男性且以素食为食物的配对
  • ...
  • 索引2:索引0:所有对是男性和有素食作为食物偏好
  • ...

我最初认为这可能是存储这些数据的最佳方式,但后来我读到了这个问题,其中指出,如果您正在实现3D列表,您可能没有正确地处理您的数据。在提到的问题中,还有一个answer,其中使用Map来存储数据,但我并不真正理解这种抽象,我不知道如何将其用于我的案例。正因为如此,我想问,如果有一个更好的,更干净的方式来存储我的数据,这样我仍然可以访问只有对男性和素食主义者等。
在代码中存在一个ArrayList<Pair> pairList,它包含所有的Pair。Pair类如下所示:

public class Pair {
    private Person person1;
    private Person person2; 
    private String foodPreference;
    private String gender;
    
    public Pair(Person person1, Person person2) {
        this.person1 = person1;
        this.person2 = person2;
        this.foodPreference = decideFoodPreference();
        this.gender = decideGender();
    }
 
    /*
      contains Getter for the fields and methods to decide and set the foodPreference
      and the gender.
    */

}

Person类包含有关特定Person的信息,如姓名、年龄、性别、位置等。
有没有更好、更干净的方式来存储Pairs,在这里我仍然可以只得到具有foodPreference x 和gender y 的Pairs,或者我的三维列表方式是我的特定数据的最佳方式?

jexiocij

jexiocij1#

虽然@Andrew S的答案有效,但我也可以建议使用Map来快速检索所需的Pairs。这将花费常数(O(1))的时间来查找,而不是过滤整个列表(O(n))。另一方面是额外的内存消耗。
要做到这一点,你必须提取一种PairAttributes类,它包含foodPreference和gender,并正确定义了equalshashCode方法。使用lombok或IDE自动生成。如果有新的属性,不要忘记更新它们。

public static class PairAttributes {
    private final String foodPreference;
    private final String gender;

    public PairAttributes(Pair pair) {
        this.foodPreference = pair.foodPreference;
        this.gender = pair.gender;
    }

    public PairAttributes(String foodPreference, String gender) {
        this.foodPreference = foodPreference;
        this.gender = gender;
    }

    // getters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PairAttributes that = (PairAttributes) o;
        return foodPreference.equals(that.foodPreference) && gender.equals(that.gender);
    }

    @Override
    public int hashCode() {
        return Objects.hash(foodPreference, gender);
    }
}

现在,只需将给定的Pairs放入HashMap,并将PairAttributes作为密钥:
一个接一个

private final Map<PairAttributes, List<Pair>> pairsByAttributes = new HashMap<>();
    ...
    pairsByAttributes.computeIfAbsent(new PairAttributes(somePair), p -> new ArrayList<>()).add(somePair);

或者如果给出一个完整的列表:

List<Pair> allPairs = List.of(new Pair(...),
        new Pair(...),
        new Pair(...),
        ...
);

Map<PairAttributes, List<Pair>> pairsByAttributes = allPairs
        .stream().
        collect(Collectors.groupingBy(PairAttributes::new));

然后得到所需的对只是get他们:

List<Pair> veggieMales = pairsByAttributes.get(
        new PairAttributes("veggie", "male")
);
yquaqz18

yquaqz182#

应该能够使用单个List<Pair>,并应用各种过滤器来获得相关对的子集。
例如:

List<Pair> allPairs = List.of(new Pair(...), 
                              new Pair(...), 
                              new Pair(...),
                              ....);

List<Pair> allMaleCarnivores = allPairs.stream()
                                  .filter(p -> p.getPerson1().isCarnivore() 
                                                 && p.getPerson2().isCarnivore()
                                                 && p.getPerson1().isMale()
                                                 && p.getPerson2().isMale()
                                          )
                                  .toList();

 // then do something with the subset
 allMaleCarnivores.forEach(p -> eatSalad(p);
qij5mzcb

qij5mzcb3#

  • "...我读了这个问题,其中指出,如果您正在实施3D列表,则可能没有正确处理数据。..."*

或多或少通常使用三维数组来表示数学矩阵。

  • "...在上面提到的问题中,也有一个答案是使用Map来存储数据,但我并不真正理解这种抽象,我不知道如何将其用于我的情况。..."*

在计算机科学中,一个 map 结构-有时也被称为 dictionary 或 * associative array *-它只是一个值列表,每个值都Map到单独的键。类似于文字 * 字典 *,它有一个单词和一个定义。
下面是关于使用 Map 集合的 *Java教程 *。

下面是一个基本的例子。

Map<String, Integer> map = new HashMap<>();
map.put("abc", 123);
map.put("def", 456);

而且,您可以使用 “key” 检索值。

map.get("abc");
  • ...是否有更好、更干净的方式来存储Pairs,其中我仍然可以只获得具有foodPreference x和gender y的Pairs,或者我的三维列表方式是我的特定数据的最佳方式?“*

是的,我来示范;您可以使用单个 ArrayList
考虑以下对象来保存数据。

class Pair {
    Person personA, personB;
}

class Person {
    String name, location;
    int age;
    Gender gender;
    FoodPreference foodPreference;
}

enum Gender {
    MALE, FEMALE, MIXED
}

enum FoodPreference {
    MEAT, VEGGIE, VEGAN
}

从这里,您可以为 PersonPair 创建新的构造函数。
此外,我在这里添加了一个 toString 方法,作为调试数据的一种方式。

class Pair {
    Person personA, personB;

    public Pair(Person personA, Person personB) {
        this.personA = personA;
        this.personB = personB;
    }
}

class Person {
    String name, location;
    int age;
    Gender gender;
    FoodPreference foodPreference;

    public Person(Gender gender, FoodPreference foodPreference) {
        this.gender = gender;
        this.foodPreference = foodPreference;
    }

    @Override
    public String toString() {
        return name;
    }
}

下面是一个向列表中添加新项的示例。

List<Pair> list = new ArrayList<>();

Person personA = new Person("personA", 1, Gender.MALE);
personA.foodPreference = FoodPreference.MEAT;
Person personB = new Person("personB", 2, Gender.FEMALE);
personB.foodPreference = FoodPreference.VEGGIE;
list.add(new Pair(personA, personB));

Person personC = new Person("personC", 3, Gender.MALE);
personC.foodPreference = FoodPreference.MEAT;
Person personD = new Person("personD", 4, Gender.FEMALE);
personD.foodPreference = FoodPreference.VEGAN;
list.add(new Pair(personC, personD));

这里是一个获取 Person 对象的示例,具有特定的值。

List<Person> find(List<Pair> list, FoodPreference foodPreference, Gender gender) {
    List<Person> matches = new ArrayList<>();
    Person person;
    for (Pair pair : list) {
        person = pair.personA;
        if (person.foodPreference == foodPreference && person.gender == gender)
            matches.add(person);
        person = pair.personB;
        if (person.foodPreference == foodPreference && person.gender == gender)
            matches.add(person);
    }
    return matches;
}

还有,用法。

List<Person> matches = find(list, FoodPreference.MEAT, Gender.MALE);

输出量

personA
personC
suzh9iv8

suzh9iv84#

在与OP进行了一些讨论之后,我认为我们缺少了一层抽象。不包括任何必要的同步关键字,因为我还不知道所有的细节。

public class PairManager {
    List<Pair> allPairs;

    // Add any cache worthy queries
    List<Pair> maleMeatEaters;

    public addPair(Pair newPair) {
        allPairs.add(newPair);
        // invalidate caches
        maleMeatEaters = null;
    }

    public removePair(Pair pair) {
        allPairs.remove(pair)
        // invalidate caches
        maleMeatEaters = null;
    }

    public List<Pair> listMaleMeatEaters() {
        return maleMeatEaters == null ? filterMaleMeatEaters() : maleMeatEaters;
    }

    private List<Pair> filterMaleMeatEaters() {
        maleMeatEaters = allPairs.stream()
            .filter(pair -> pair.getGender() == Gender.MALE)
            .filter(pair -> pair.getFoodPreference() == FoodPreference.MEAT)
            .collectList(); // pre-Java17 .collect(Collectors.toList());
        return maleMeatEaters;
    }
}

相关问题