Java SE 8是否有Pairs或Tuples?

时间:2014-06-20 13:40:40

标签: java lambda functional-programming java-8 java-stream

我正在玩Java SE 8中的延迟功能操作,我希望map索引i到一对/元组(i, value[i]),然后基于filter在第二个value[i]元素上,最后只输出索引。

我是否仍然要忍受这个:What is the equivalent of the C++ Pair<L,R> in Java?在兰德和溪流的大胆新时代?

更新:我提供了一个相当简化的示例,其中有一个由@dkatzel提供的简洁解决方案,其中一个答案如下。但是,概括。因此,让我添加一个更一般的例子:

package com.example.test;

import java.util.ArrayList;
import java.util.stream.IntStream;

public class Main {

  public static void main(String[] args) {
    boolean [][] directed_acyclic_graph = new boolean[][]{
        {false,  true, false,  true, false,  true},
        {false, false, false,  true, false,  true},
        {false, false, false,  true, false,  true},
        {false, false, false, false, false,  true},
        {false, false, false, false, false,  true},
        {false, false, false, false, false, false}
    };

    System.out.println(
        IntStream.range(0, directed_acyclic_graph.length)
        .parallel()
        .mapToLong(i -> IntStream.range(0, directed_acyclic_graph[i].length)
            .filter(j -> directed_acyclic_graph[j][i])
            .count()
        )
        .filter(n -> n == 0)
        .collect(() -> new ArrayList<Long>(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2))
    );
  }

}

这会给[0, 0, 0]不正确的输出,该输出对应于全部为false的三列的计数。我需要的是这三列的索引。正确的输出应为[0, 2, 4]。我怎样才能得到这个结果?

10 个答案:

答案 0 :(得分:182)

更新:这个答案是对原始问题的回应, Java SE 8是否有Pairs或Tuples?(如果不是,为什么不这样做?) OP已经用更完整的示例更新了问题,但似乎可以在不使用任何类型的Pair结构的情况下解决它。 [OP注意:这里是the other correct answer。]


简短的回答是否定的。您要么必须自己动手,要么引入实现它的几个库中的一个。

建议在Java SE中使用Pair类,并至少拒绝一次。请参阅其中一个OpenJDK邮件列表中的this discussion thread。权衡并不明显。一方面,在其他库和应用程序代码中有许多Pair实现。这表明了一种需求,将这样的类添加到Java SE将增加重用和共享。另一方面,拥有一个Pair类会增加从Pairs和集合中创建复杂数据结构的诱惑,而不会创建必要的类型和抽象。 (这是该帖子中Kevin Bourillion's message的解释。)

我建议大家阅读整个电子邮件主题。它非常富有洞察力,没有任何影响。这很有说服力。当它开始时,我想,&#34;是的,Java SE中应该有一个Pair类&#34;但是当线程到达终点时,我改变了主意。

但请注意,JavaFX具有javafx.util.Pair类。 JavaFX的API与Jav​​a SE API分开演变。

从链接问题What is the equivalent of the C++ Pair in Java?可以看出,围绕显然是如此简单的API的设计空间相当大。对象应该是不可变的吗?它们应该可序列化吗?他们应该具有可比性吗班级应该是最终的吗?这两个元素应该订购吗?它应该是一个接口还是一个类?为什么要成对停止?为什么不是三元组,四元组或N元组?

当然,元素的命名也是不可避免的:

  • (a,b)
  • (第一,第二)
  • (左,右)
  • (car,cdr)
  • (foo,bar)

几乎没有提到过的一个大问题是Pairs与原始人的关系。如果您有一个表示2D空间中某个点的(int x, int y)基准面,则将其表示为Pair<Integer, Integer>消耗三个对象而不是两个32位字。此外,这些对象必须驻留在堆上,并且会产生GC开销。

似乎很清楚,像Streams一样,对于原始的专业化对于Pairs来说是至关重要的。我们想看看:

Pair
ObjIntPair
ObjLongPair
ObjDoublePair
IntObjPair
IntIntPair
IntLongPair
IntDoublePair
LongObjPair
LongIntPair
LongLongPair
LongDoublePair
DoubleObjPair
DoubleIntPair
DoubleLongPair
DoubleDoublePair

即使IntIntPair仍然需要堆上的一个对象。

这些当然让人联想到Java SE 8中java.util.function包中功能接口的激增。如果你不想要一个膨胀的API,那么你会遗漏哪些?您也可以认为这还不够,还应该添加Boolean的专业化。

我的感觉是,如果Java很久以前就添加了一个Pair类,那么它本来就很简单,甚至过于简单,并且它不会满足我们现在想象的许多用例。考虑如果在JDK 1.0时间框架中添加了Pair,它可能是可变的! (看看java.util.Date。)人们会对此感到高兴吗?我的猜测是,如果在Java中有一个Pair类,它将是有点排序 - 并非真正有用,并且每个人仍然会自己滚动以满足他们的需求,在外部库中会有各种Pair和Tuple实现,人们仍然在争论/讨论如何修复Java的Pair类。换句话说,就像今天我们在同一个地方。

与此同时,一些工作正在解决根本问题,即在值类型的JVM(以及最终的Java语言)中提供更好的支持。请参阅此State of the Values文档。这是初步的,推测性的工作,它只涵盖了JVM角度的问题,但它背后已有相当多的思考。当然,不能保证这会进入Java 9,或者无论何时进入Java 9,但它确实显示了当前关于这个主题的思考方向。

答案 1 :(得分:30)

您可以查看这些内置类:

答案 2 :(得分:16)

遗憾的是,Java 8没有引入对或元组。当然,您总是可以使用org.apache.commons.lang3.tuple(我个人与Java 8结合使用),或者您可以创建自己的包装器。或使用地图。或者像这样的东西,正如accepted answer中对你所链接的那个问题所解释的那样。

答案 3 :(得分:14)

似乎可以在不使用任何类型的Pair结构的情况下解决完整示例。关键是过滤列索引,谓词检查整个列,而不是将列索引映射到该列中false个条目的数量。

执行此操作的代码在此处:

    System.out.println(
        IntStream.range(0, acyclic_graph.length)
            .filter(i -> IntStream.range(0, acyclic_graph.length)
                                  .noneMatch(j -> acyclic_graph[j][i]))
            .boxed()
            .collect(toList()));

这导致[0, 2, 4]的输出,我认为OP请求的正确结果。

另请注意boxed()操作将int值格式化为Integer个对象。这使得人们可以使用预先存在的toList()收集器而不必写出自己进行装箱的收集器函数。

答案 4 :(得分:6)

由于您只关心索引,因此根本不需要映射到元组。为什么不编写一个使用数组中查找元素的过滤器?

     int[] value =  ...


IntStream.range(0, value.length)
            .filter(i -> value[i] > 30)  //or whatever filter you want
            .forEach(i -> System.out.println(i));

答案 5 :(得分:5)

Map.Entry可用作Pair

不幸的是,它对Java 8流没有帮助,因为问题在于即使lambdas可以使用多个参数,Java语言也只允许返回单个值(对象或基本类型)。这意味着无论何时有流,最终都会从上一个操作传递一个对象。这是Java语言的缺失,因为如果支持多个返回值并且流支持它们,我们可以通过流完成更好的非平凡任务。

在此之前,只有很少用处。

编辑2018-02-12:在处理项目时,我编写了一个帮助程序类,它有助于处理稍后在您需要的流中具有标识符的特殊情况,但其间的流部分不知道它。在我自行发布之前,我可以IdValue.javaIdValueTest.java进行单元测试

答案 6 :(得分:2)

Vavr(以前称为Javaslang)http://www.vavr.io)也提供元组(直到8的大小)。这是javadoc:https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html

这是一个简单的例子:

Tuple2<Integer, String> entry = Tuple.of(1, "A");

Integer key = entry._1;
String value = entry._2;

为什么JDK本身没有带有一个简单的元组直到现在对我来说是一个谜。编写包装类似乎是日常业务。

答案 7 :(得分:2)

Eclipse Collections具有Pair以及所有原始/对象对的所有组合(适用于所有八个基元)。

Tuples工厂可以创建Pair的实例,PrimitiveTuples工厂可用于创建基元/对象组的所有组合。

我们在Java 8发布之前添加了这些。它们对于我们的原始映射实现键/值迭代器很有用,我们也支持所有原始/对象组合。

如果您愿意添加额外的库开销,可以使用Stuart接受的解决方案并将结果收集到原始IntList中以避免装箱。我们在Eclipse Collections 9.0中添加了新方法,以允许从Int/Long/Double Streams创建Int/Long/Double个集合。

IntList list = IntLists.mutable.withAll(intStream);

注意:我是Eclipse Collections的提交者。

答案 8 :(得分:1)

从Java 9开始,您可以比以前更轻松地创建Map.Entry的实例:

Entry<Integer, String> pair = Map.entry(1, "a");

Map.entry返回不可修改的Entry,并禁止null。

答案 9 :(得分:0)

我不是Java方面的专家,只是一个学生,但是我做出了变通办法来像python中那样模拟元组:

class C1 { 
    String name; int X,Y;
    C1(List l){name = ""+l.get(0); X=(int)l.get(1); Y=(int)l.get(2);}
}

class MyClass{
    List l2= List.of(List.of("a",7,9), List.of("b",8,4), List.of("c",3,6)); //here
    List l3=new ArrayList();
    for (Object li: l2) l3.add(new C1((List)li));
}

希望这会有所帮助。