Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/390.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用流合并两个集合,但仅使用唯一值,并使用谓词而不是相等?_Java_Collections_Java 8_Java Stream_Java 9 - Fatal编程技术网

Java 使用流合并两个集合,但仅使用唯一值,并使用谓词而不是相等?

Java 使用流合并两个集合,但仅使用唯一值,并使用谓词而不是相等?,java,collections,java-8,java-stream,java-9,Java,Collections,Java 8,Java Stream,Java 9,我正在尝试合并两个集合,但是在我只想添加唯一值的地方有条件地进行合并。而什么构成了唯一性应该由谓词(或类似谓词)决定,而不是等于函数 例如,假设我们有以下两个Person对象集合: List<Employee> list1 = Arrays.asList(new Employee(1, "Adam", "Smith", Type.CEO), new Employee(2, "Bob", "Jones", Type.OfficeManager), new Employee(3, "Ca

我正在尝试合并两个集合,但是在我只想添加唯一值的地方有条件地进行合并。而什么构成了唯一性应该由谓词(或类似谓词)决定,而不是等于函数

例如,假设我们有以下两个Person对象集合:

List<Employee> list1 = Arrays.asList(new Employee(1, "Adam", "Smith", Type.CEO), new Employee(2, "Bob", "Jones", Type.OfficeManager), new Employee(3, "Carl", "Lewis", Type.SalesPerson));

List<Employee> list2 = Arrays.asList(new Employee(4, "Xerxes", "Brown", Type.OfficeManager), new Employee(5, "Yuri", "Gagarin", Type.Janitor), new Employee(6, "Zain", "Wilson", Type.SalesPerson));
以一般的Java8/9方式实现这一点的“最佳”方法是什么?也就是说,我不想写一些特定于Person对象或类型enum的东西,也不想写一些使用对象的equals方法的东西。相反,我想使用双预测或类似的东西

但是,我也不想自己执行任何循环Streams似乎是个不错的选择,但我不知道如何实现这一点。如果一个值来自一个流,而另一个值来自另一个流,我如何编写一个双预测,而不用自己执行循环

我想使用BiPredicate(或类似)的原因是,我希望能够将此函数与高级逻辑一起使用,在高级逻辑中,不可能简单地提取所有元素的某些属性,然后根据此属性的唯一性对值进行分组

有什么建议吗

/吉米

更新:为了澄清我为什么要谈论谓词,这里有一个更复杂的例子:

假设我们有两个Employee对象集合,与前面一样。但这一次,唯一性逻辑无法使用映射函数表示为Employee对象的特定属性。相反,它使用了EmployeeRegistry中的一些数据,如:如果两名员工属于相同的纳税等级如果他们属于相同的“类型”,则认为他们是平等的。由于这一点或逻辑,不可能将其简化为用于分组数据或类似内容的唯一键

Update2:为了简单起见,下面是一个不太复杂的示例,但它仍然足够复杂,不能简单地映射到字段。这有点做作,但那是为了简单

假设我们有两个字符串集合。唯一性的计算如下:

  • 如果两个字符串长度相等,则认为它们相等
  • 否则,如果两个字符串以相同字符开头,则认为它们相等
使用Federico Peralta Schaffner所建议的方法Collectors.toMap,似乎是可行的,尽管我不确定如何编写一个既符合标准又高效的
hashCode()
实现。我能想到的唯一一个函数实现是返回一个常量值(即不管字符串是什么,都返回相同的值)

更新3:考虑到我的“equalness”算法的或逻辑打破了等于契约,并且使得编写有效的哈希代码实现变得困难(不可能?),我现在回到了我开始的地方。例如需要某种类型的谓词。下面是一个更新的“真实世界”示例:

假设我们像以前一样拥有两个Employee对象集合,并且希望将这些集合合并为一个。但这一次,我们希望避免包括不合群的人。为了确定两个人是否相处融洽,我们有一个HumanRelationsDepartment对象,方法是IsokToWorkwithther(Person,Person)。如果检测到两个相处不融洽的人,则只将其中一个添加到新集合中。哪一个可以由映射函数确定,默认逻辑可能是选择第一个人


编写解决这个问题的老派代码相当简单。我要寻找的是一个基于无循环流的解决方案。这样的解决方案存在吗?性能不是问题。

将它们映射到具有唯一值作为键的映射,然后将条目映射到列表。

将它们映射到具有唯一值作为键的映射,然后将条目映射到列表。

对于两个流的简单合并,可以使用concat(只需更新reducer的逻辑):

// Concatenate the streams
Stream.concat(list1.stream(), list2.stream())
    .collect(
        // Collect similar employees together
        groupingBy(
            // Where "similar" is determined by a function, e.g. Employee::getType
            keyFn,
            // Take the first employee found
            (a, b) -> a)
    // Discard the keys.
    .values();
2-声明将在同一索引处合并2个对象的“减缩器”:


要简化它:创建一个通用的
zip
方法:

public static <T> List<T> zipStreams(List<T> list1, List<T> list2, BiFunction<T, T, T> employeeMerger, Comparator<T> sortComparator) {

    if(list1.size() != list2.size()) {
        throw new IllegalArgumentException("Lists must be of the same length");
    }

    List<T> list1Sorted = sortComparator == null ? list1: list1.stream()
                    .sorted(sortComparator)
                    .collect(Collectors.toList()), 
       list2Sorted = sortComparator == null ? list2: list2.stream()
                    .sorted(sortComparator)
                    .collect(Collectors.toList());

    return IntStream.range(0,  list1Sorted.size())
            .mapToObj(i -> Arrays.<T>asList(list1Sorted.get(i), list2Sorted.get(i)))
            .map(list -> employeeMerger.apply(list.get(0), list.get(1)))
            .collect(Collectors.toList());
}

对于两个流的简单合并,可以使用concat(只需更新reducer的逻辑):

2-声明将在同一索引处合并2个对象的“减缩器”:


要简化它:创建一个通用的
zip
方法:

public static <T> List<T> zipStreams(List<T> list1, List<T> list2, BiFunction<T, T, T> employeeMerger, Comparator<T> sortComparator) {

    if(list1.size() != list2.size()) {
        throw new IllegalArgumentException("Lists must be of the same length");
    }

    List<T> list1Sorted = sortComparator == null ? list1: list1.stream()
                    .sorted(sortComparator)
                    .collect(Collectors.toList()), 
       list2Sorted = sortComparator == null ? list2: list2.stream()
                    .sorted(sortComparator)
                    .collect(Collectors.toList());

    return IntStream.range(0,  list1Sorted.size())
            .mapToObj(i -> Arrays.<T>asList(list1Sorted.get(i), list2Sorted.get(i)))
            .map(list -> employeeMerger.apply(list.get(0), list.get(1)))
            .collect(Collectors.toList());
}

您可以通过以下方式实现您的目标:

Collection merged=Stream.of(列表1、列表2)
.flatMap(集合::流)
.collect(Collectors.toMap(e->calculateGroup(e),e->e,(e1,e2)->e1)))
.values();
因此,这将根据一些
calculateGroup
方法创建一个
Map
,该方法接收
Employee
实例并返回表示
员工所属组的内容。这可能是
员工
的某些属性,即
类型
,也可能是更复杂的属性,可以从其他地方获取数据以根据员工的年收入确定集团,即税级。这是地图的钥匙,它将根据您的具体需要确定唯一性。这种方法的唯一要求是,无论您对键使用什么类,它都必须一致地实现
equals
hashCode

映射的值将只是连接流的
Employee
实例。对于合并函数(
Collectors.toMap
3参数),我使用了
(
List<Employee> list1Sorted = list1.stream()
       .sorted(Comparator.comparing(Employee::getType))
       .collect(Collectors.toList());

List<Employee> list2Sorted = list2.stream()
       .sorted(Comparator.comparing(Employee::getType))
       .collect(Collectors.toList());
//This is returning an arbitrary value. You may want to add your own logic:
BiFunction<Employee, Employee, Employee> reducer = (e1, e2) -> e1;
List<Employee> mergedList = IntStream.range(0,  list1.size())
    .mapToObj(i -> new Employee[] {list1Sorted.get(i), list2Sorted.get(i)})
    .map(e -> reducer.apply(e[0], e[1]))
    .collect(Collectors.toList());
public static <T> List<T> zipStreams(List<T> list1, List<T> list2, BiFunction<T, T, T> employeeMerger, Comparator<T> sortComparator) {

    if(list1.size() != list2.size()) {
        throw new IllegalArgumentException("Lists must be of the same length");
    }

    List<T> list1Sorted = sortComparator == null ? list1: list1.stream()
                    .sorted(sortComparator)
                    .collect(Collectors.toList()), 
       list2Sorted = sortComparator == null ? list2: list2.stream()
                    .sorted(sortComparator)
                    .collect(Collectors.toList());

    return IntStream.range(0,  list1Sorted.size())
            .mapToObj(i -> Arrays.<T>asList(list1Sorted.get(i), list2Sorted.get(i)))
            .map(list -> employeeMerger.apply(list.get(0), list.get(1)))
            .collect(Collectors.toList());
}
zipStreams(list1, list2, (e1, e2) -> e1, Comparator.comparing(Employee::getType));
Collection<Employee> merged = Stream.of(list1, list2)
    .flatMap(Collection::stream)
    .collect(Collectors.toMap(e -> calculateGroup(e), e -> e, (e1, e2) -> e1)))
    .values();