Java SE 8 有 Pairs 或 Tuples 吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24328679/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:35:14  来源:igfitidea点击:

Does Java SE 8 have Pairs or Tuples?

javalambdafunctional-programmingjava-8java-stream

提问by necromancer

I am playing around with lazy functional operations in Java SE 8, and I want to mapan index ito a pair / tuple (i, value[i]), then filterbased on the second value[i]element, and finally output just the indices.

我在 Java SE 8 中玩懒惰的函数操作,我想要一个 pair / tuplemap的索引,然后基于第二个元素,最后只输出索引。i(i, value[i])filtervalue[i]

Must I still suffer this: What is the equivalent of the C++ Pair<L,R> in Java?in the bold new era of lambdas and streams?

我还必须忍受这个:Java 中 C++ Pair<L,R> 的等价物是什么?在 lambdas 和流的大胆新时代?

Update:I presented a rather simplified example, which has a neat solution offered by @dkatzel in one of the answers below. However, it does notgeneralize. Therefore, let me add a more general example:

更新:我提出了一个相当简单的例子,它有一个由@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))
    );
  }

}

This gives incorrectoutput of [0, 0, 0]which corresponds to the countsfor the three columns that are all false. What I need are the indicesof these three columns. The correct output should be [0, 2, 4]. How can I get this result?

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

采纳答案by Stuart Marks

UPDATE:This answer is in response to the original question, Does Java SE 8 have Pairs or Tuples?(And implicitly, if not, why not?) The OP has updated the question with a more complete example, but it seems like it can be solved without using any kind of Pair structure. [Note from OP: here is the other correct answer.]

更新:这个答案是对原始问题的回应,Java SE 8 是否有对或元组?(并且隐含地,如果没有,为什么不呢?)OP 已经用一个更完整的例子更新了这个问题,但似乎可以在不使用任何类型的 Pair 结构的情况下解决它。[来自 OP 的注释:这是另一个正确答案。]



The short answer is no. You either have to roll your own or bring in one of the several libraries that implements it.

最简洁的答案是不。你要么自己动手,要么引入实现它的几个库之一。

Having a Pairclass in Java SE was proposed and rejected at least once. See this discussion threadon one of the OpenJDK mailing lists. The tradeoffs are not obvious. On the one hand, there are many Pair implementations in other libraries and in application code. That demonstrates a need, and adding such a class to Java SE will increase reuse and sharing. On the other hand, having a Pair class adds to the temptation of creating complicated data structures out of Pairs and collections without creating the necessary types and abstractions. (That's a paraphrase of Kevin Bourillion's messagefrom that thread.)

Pair在 Java SE 中创建一个类至少被提议和拒绝过一次。请参阅OpenJDK 邮件列表之一上的此讨论主题。权衡并不明显。一方面,在其他库和应用程序代码中有许多 Pair 实现。这表明了一种需求,将这样的类添加到 Java SE 将增加重用和共享。另一方面,拥有 Pair 类会增加使用 Pairs 和集合创建复杂数据结构而不创建必要类型和抽象的诱惑。(这是对Kevin Bourillion来自该线程的消息的转述。)

I recommend everybody read that entire email thread. It's remarkably insightful and has no flamage. It's quite convincing. When it started I thought, "Yeah, there should be a Pair class in Java SE" but by the time the thread reached its end I had changed my mind.

我建议大家阅读整个电子邮件线程。它非常有见地,而且没有火焰。这很有说服力。当它开始时,我想,“是的,Java SE 中应该有一个 Pair 类”,但是当线程结束时我改变了主意。

Note however that JavaFX has the javafx.util.Pairclass. JavaFX's APIs evolved separately from the Java SE APIs.

但是请注意,JavaFX 具有javafx.util.Pair类。JavaFX 的 API 与 Java SE API 分开发展。

As one can see from the linked question What is the equivalent of the C++ Pair in Java?there is quite a large design space surrounding what is apparently such a simple API. Should the objects be immutable? Should they be serializable? Should they be comparable? Should the class be final or not? Should the two elements be ordered? Should it be an interface or a class? Why stop at pairs? Why not triples, quads, or N-tuples?

从链接的问题可以看出,Java 中的 C++ 对是什么?有相当大的设计空间围绕着显然如此简单的 API。对象应该是不可变的吗?它们应该是可序列化的吗?它们应该具有可比性吗?课程是否应该是最终的?应该对这两个元素进行排序吗?它应该是一个接口还是一个类?为什么要成对停止?为什么不是三元组、四元组或 N 元组?

And of course there is the inevitable naming bikeshed for the elements:

当然还有不可避免的元素命名自行车棚:

  • (a, b)
  • (first, second)
  • (left, right)
  • (car, cdr)
  • (foo, bar)
  • etc.
  • (a, b)
  • (第一秒)
  • (左右)
  • (汽车,CDR)
  • (富,酒吧)
  • 等等。

One big issue that has hardly been mentioned is the relationship of Pairs to primitives. If you have an (int x, int y)datum that represents a point in 2D space, representing this as Pair<Integer, Integer>consumes three objectsinstead of two 32-bit words. Furthermore, these objects must reside on the heap and will incur GC overhead.

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

It would seem clear that, like Streams, it would be essential for there to be primitive specializations for Pairs. Do we want to see:

很明显,像 Streams 一样,对 Pairs 的原始专业化是必不可少的。我们想看:

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

Even an IntIntPairwould still require one object on the heap.

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

These are, of course, reminiscent of the proliferation of functional interfaces in the java.util.functionpackage in Java SE 8. If you don't want a bloated API, which ones would you leave out? You could also argue that this isn't enough, and that specializations for, say, Booleanshould be added as well.

当然,这些让人想起java.util.functionJava SE 8 包中函数式接口的激增。如果您不想要臃肿的 API,您会忽略哪些 API?您还可以争辩说,这还不够,还Boolean应该添加专业化,例如。

My feeling is that if Java had added a Pair class long ago, it would have been simple, or even simplistic, and it wouldn't have satisfied many of the use cases we are envisioning now. Consider that if Pair had been added in the JDK 1.0 time frame, it probably would have been mutable! (Look at java.util.Date.) Would people have been happy with that? My guess is that if there were a Pair class in Java, it would be kinda-sort-not-really-useful and everybody will still be rolling their own to satisfy their needs, there would be various Pair and Tuple implementations in external libraries, and people would still be arguing/discussing about how to fix Java's Pair class. In other words, kind of in the same place we're at today.

我的感觉是,如果 Java 早就添加了 Pair 类,它会很简单,甚至简单化,它不会满足我们现在设想的许多用例。考虑一下,如果在 JDK 1.0 时间框架中添加了 Pair,它可能是可变的!(看看 java.util.Date。)人们会对此感到满意吗?我的猜测是,如果 Java 中有一个 Pair 类,它会有点不太有用,每个人仍然会自己滚动以满足他们的需求,外部库中会有各种 Pair 和 Tuple 实现,人们仍然会争论/讨论如何修复 Java 的 Pair 类。换句话说,有点像我们今天所处的地方。

Meanwhile, some work is going on to address the fundamental issue, which is better support in the JVM (and eventually the Java language) for value types. See this State of the Valuesdocument. This is preliminary, speculative work, and it covers only issues from the JVM perspective, but it already has a fair amount of thought behind it. Of course there are no guarantees that this will get into Java 9, or ever get in anywhere, but it does show the current direction of thinking on this topic.

同时,一些工作正在进行以解决基本问题,即在 JVM(以及最终的 Java 语言)中更好地支持值类型。请参阅此价值观文件状态。这是初步的、推测性的工作,它只涵盖了 JVM 角度的问题,但它背后已经有相当多的想法。当然,不能保证这会进入 Java 9,或者永远进入任何地方,但它确实显示了当前对这个主题的思考方向。

回答by blalasaadri

Sadly, Java 8 did not introduce pairs or tuples. You can always use org.apache.commons.lang3.tupleof course (which personally I do use in combination with Java 8) or you can create your own wrappers. Or use Maps. Or stuff like that, as is explained in the accepted answerto that question you linked to.

遗憾的是,Java 8 没有引入对或元组。当然,您始终可以使用org.apache.commons.lang3.tuple(我个人确实将它与 Java 8 结合使用),或者您可以创建自己的包装器。或者使用地图。或者类似的东西,正如您链接到的那个问题的公认答案中所解释的那样。



UPDATE:JDK 14 is introducing recordsas a preview feature. These aren't tuples, but can be used to save many of the same problems. In your specific example from above, that could look something like this:

更新:JDK 14 将记录作为预览功能引入。这些不是元组,但可以用来解决许多相同的问题。在上面的具体示例中,它可能如下所示:

public class Jdk14Example {
    record CountForIndex(int index, long count) {}

    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()
                        .mapToObj(i -> {
                            long count = IntStream.range(0, directed_acyclic_graph[i].length)
                                            .filter(j -> directed_acyclic_graph[j][i])
                                            .count();
                            return new CountForIndex(i, count);
                        }
                        )
                        .filter(n -> n.count == 0)
                        .collect(() -> new ArrayList<CountForIndex>(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2))
        );
    }
}

When compiled and run with JDK 14(at the time of writing, this an early access build) using the --enable-previewflag, you get the following result:

当使用JDK 14编译并运行(在撰写本文时,这是一个早期访问版本)时--enable-preview,您会得到以下结果:

[CountForIndex[index=0, count=0], CountForIndex[index=2, count=0], CountForIndex[index=4, count=0]]

回答by dkatzel

Since you only care about the indexes, you don't need to map to tuples at all. Why not just write a filter that uses the looks up elements in your array?

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

     int[] value =  ...


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

回答by Stuart Marks

It appears that the full example can be solved without the use of any kind of Pair structure. The key is to filter on the column indexes, with the predicate checking the entire column, instead of mapping the column indexes to the number of falseentries in that column.

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

The code that does this is here:

执行此操作的代码在这里:

    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()));

This results in output of [0, 2, 4]which is I think the correct result requested by the OP.

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

Also note the boxed()operation that boxes the intvalues into Integerobjects. This enables one to use the pre-existing toList()collector instead having to write out collector functions that do the boxing themselves.

还要注意将值boxed()装箱intInteger对象中的操作。这使人们能够使用预先存在的toList()收集器,而不必编写自己进行装箱的收集器函数。

回答by Thorbj?rn Ravn Andersen

Yes.

是的。

Map.Entrycan be used as a Pair.

Map.Entry可以用作Pair.

Unfortunately it does not help with Java 8 streams as the problem is that even though lambdas can take multiple arguments, the Java language only allows for returning a single value (object or primitive type). This implies that whenever you have a stream you end up with being passed a single object from the previous operation. This is a lack in the Java language, because if multiple return values was supported AND streams supported them we could have much nicer non-trivial tasks done by streams.

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

Until then, there is only little use.

在此之前,只有很少的用途。

EDIT 2018-02-12: While working on a project I wrote a helper class which helps handling the special case of having an identifier earlier in the stream you need at a later time but the stream part in between does not know about it. Until I get around to release it on its own it is available at IdValue.javawith a unit test at IdValueTest.java

编辑 2018-02-12:在处理一个项目时,我编写了一个帮助程序类,它有助于处理在稍后需要的流中较早拥有标识符的特殊情况,但中间的流部分不知道它。直到我避开释放它自身的它是在IdValue.java与单元测试IdValueTest.java

回答by senerh

You can have a look on these built-in classes :

您可以查看这些内置类:

回答by wumpz

Vavr (formerly called Javaslang)(http://www.vavr.io) provides tuples (til size of 8) as well. Here is the javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html.

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

This is a simple example:

这是一个简单的例子:

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

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

Why JDK itself did not come with a simple kind of tuples til now is a mystery to me. Writing wrapper classes seems to be an every day business.

为什么 JDK 本身直到现在还没有提供一种简单的元组,这对我来说是个谜。编写包装类似乎是一项日常工作。

回答by Donald Raab

Eclipse Collections has Pairand all combinations of primitive/object Pairs (for all eight primitives).

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

The Tuplesfactory can create instances of Pair, and the PrimitiveTuplesfactory can be used to create all combinations of primitive/object pairs.

Tuples工厂可以创建的实例Pair,并且PrimitiveTuples工厂可以用来创建原始/对象对所有组合。

We added these before Java 8 was released. They were useful to implement key/value Iterators for our primitive maps, which we also support in all primitive/object combinations.

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

If you're willing to add the extra library overhead, you can use Stuart's accepted solution and collect the results into a primitive IntListto avoid boxing. We added new methods in Eclipse Collections 9.0to allow for Int/Long/Doublecollections to be created from Int/Long/DoubleStreams.

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

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

Note: I am a committer for Eclipse Collections.

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

回答by ZhekaKozlov

Since Java 9, you can create instances of Map.Entryeasier than before:

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

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

Map.entryreturns an unmodifiable Entryand forbids nulls.

Map.entry返回不可修改Entry并禁止空值。