小编Fed*_*ner的帖子

递归使用Stream.flatMap()

考虑以下课程:

public class Order {

    private String id;

    private List<Order> orders = new ArrayList<>();

    @Override
    public String toString() {
        return this.id;
    }

    // getters & setters
}
Run Code Online (Sandbox Code Playgroud)

注意:重要的是要注意我无法修改此类,因为我正在从外部API中使用它.

还要考虑以下订单层次结构:

Order o1 = new Order();
o1.setId("1");
Order o11 = new Order();
o11.setId("1.1");
Order o111 = new Order();
o111.setId("1.1.1");
List<Order> o11Children = new ArrayList<>(Arrays.asList(o111));
o11.setOrders(o11Children);

Order o12 = new Order();
o12.setId("1.2");
List<Order> o1Children = new ArrayList<>(Arrays.asList(o11, o12));
o1.setOrders(o1Children);

Order o2 = new Order();
o2.setId("2");
Order o21 = new …
Run Code Online (Sandbox Code Playgroud)

java java-8 java-stream

30
推荐指数
2
解决办法
1万
查看次数

lambdas中隐含的匿名类型

这个问题中,用户@Holger提供了一个答案,显示了匿名类的不常见用法,我不知道.

该答案使用流,但这个问题不是关于流,因为这个匿名类型构造可以在其他上下文中使用,即:

String s = "Digging into Java's intricacies";

Optional.of(new Object() { String field = s; })
    .map(anonymous -> anonymous.field) // anonymous implied type 
    .ifPresent(System.out::println);
Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,这会编译并打印预期的输出.


注意:我很清楚,自古以来,可以构造一个匿名内部类并使用其成员,如下所示:

int result = new Object() { int incr(int i) {return i + 1; } }.incr(3);
System.out.println(result); // 4
Run Code Online (Sandbox Code Playgroud)

但是,这不是我在这里问的问题.我的情况不同,因为匿名类型是通过Optional方法链传播的.


现在,我可以想象这个功能的一个非常有用的用法......很多时候,我需要mapStream管道上发出一些操作,同时保留原始元素,即假设我有一个人员列表:

public class Person {
    Long id;
    String name, lastName;
    // getters, setters, hashCode, equals...
}

List<Person> people = ...;
Run Code Online (Sandbox Code Playgroud)

而且我需要Person在某些存储库中存储我的实例的JSON表示,为此我需要每个Person …

java lambda anonymous-types language-lawyer java-8

21
推荐指数
1
解决办法
2054
查看次数

这是如何编译的?

我正在编写一个函数,它接受一个keyExtractor函数列表来生成一个Comparator(想象我们有一个具有许多属性的对象,并希望能够以任意顺序任意比较大量属性).

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

class Test {
    public static <T, S extends Comparable<S>> Comparator<T> parseKeysAscending(List<Function<T, S>> keyExtractors) {
        if (keyExtractors.isEmpty()) {
            return (a, b) -> 0;
        } else {
            Function<T, S> firstSortKey = keyExtractors.get(0);
            List<Function<T, S>> restOfSortKeys = keyExtractors.subList(1, keyExtractors.size());
            return Comparator.comparing(firstSortKey).thenComparing(parseKeysAscending(restOfSortKeys));
        }
    }

    public static void main(String[] args) {
        List<Extractor<Data, ?>> extractors = new ArrayList<>();
        extractors.add(new Extractor<>(Data::getA));
        extractors.add(new Extractor<>(Data::getB));

        Comparator<Data> test = parseKeysAscending(
                extractors.stream()
                        .map(e -> e)
                        .collect(Collectors.toList()));
    }

}


class Extractor<T, …
Run Code Online (Sandbox Code Playgroud)

java generics functional-programming comparator java-8

19
推荐指数
2
解决办法
1333
查看次数

什么类型的令牌恰好是Java 10中的"var"?

在最后一期Heinz Kabutz的时事通讯#255 Java 10:推断的局部变量中,显示它var不是Java 10中的保留字,因为您也可以使用它var作为标识符:

public class Java10 {
    var var = 42; // <-- this works
}
Run Code Online (Sandbox Code Playgroud)

但是,您不能使用ie assert作为标识符var assert = 2,因为它assert是一个保留字.

正如在链接的时事通讯中所说的那样,var不是保留字的事实是好消息,因为这允许来自Java的先前版本的代码var用作标识符在Java 10中编译而没有问题.

那么,那是什么var?它既不是显式类型也不是语言的保留字,因此它被允许作为标识符,但是当它用于在Java 10中声明局部变量时它确实具有特殊含义.我们究竟在一个上下文中调用它局部变量声明?

此外,除了支持向后兼容性(通过允许包含var作为标识符的旧代码进行编译),还有其他优点是var 不是保留字吗?

java java-10

18
推荐指数
2
解决办法
1264
查看次数

泛型类型变量中的局部类型推断和逆变

我来自以下代码:

public static <T> Set<T> distinct(
        Collection<? extends T> list,
        Comparator<? super T> comparator) {

    Set<T> set = new TreeSet<>(comparator);
    set.addAll(list);
    return set;
}
Run Code Online (Sandbox Code Playgroud)

此代码仅使用中间项TreeSet来删除重复项,其中元素之间的相等性按照提供的比较器进行定义.

让我们给本地类型推理一个机会,我(天真地)想...所以我将上面的代码更改为:

public static <T> Set<T> distinct(
        Collection<? extends T> list,
        Comparator<? super T> comparator) {

    var set = new TreeSet<>(comparator);
    set.addAll(list);
    return set;
}
Run Code Online (Sandbox Code Playgroud)

这对我来说很有意义,因为set可以从类型中推断出类型comparator,或者我认为.但是,修改后的代码不会编译并生成以下错误:

java: incompatible types: java.util.TreeSet<capture#1 of ? super T> cannot be converted to java.util.Set<T>
Run Code Online (Sandbox Code Playgroud)

现在,我理解为什么会出现错误,并且我承认比较器Comparator<? super T>的类型实际上是,所以推断的类型varTreeSet<? super T>. …

java generics type-inference contravariance java-10

15
推荐指数
1
解决办法
455
查看次数

java中的条件方法链接

将方法链接在一起的最佳方法是什么?在我的场景中,有四种方法.如果一种方法的输出是true,则必须调用第二种方法.

例如:

flag = method1();
if (flag){
  flag = method2();
}
if (flag){
  method3();
} 
// and so on for next methods ..
Run Code Online (Sandbox Code Playgroud)

java

13
推荐指数
3
解决办法
1313
查看次数

关于不可变集和映射的JDK9随机化

阅读这个问题Eugene给出的答案,我发现JDK9不可变集和映射将引入一个随机性源,它将影响它们的遍历.这意味着迭代顺序确实是随机的,至少在JVM的不同运行中是这样.

由于规范不保证集合和映射的任何遍历/迭代顺序,这绝对没问题.实际上,代码绝不能依赖于特定于实现的细节,而是依赖于规范.

我知道今天,使用JDK 8,如果我有一个HashSet并且这样做(取自链接的答案):

Set<String> wordSet = new HashSet<>(Arrays.asList("just", "a", "test"));

System.out.println(wordSet);

for (int i = 0; i < 100; i++) {
    wordSet.add("" + i);
}

for (int i = 0; i < 100; i++) {
    wordSet.remove("" + i);
}

System.out.println(wordSet);
Run Code Online (Sandbox Code Playgroud)

然后元素的迭代顺序将改变,两个输出将不同.这是因为向集合中添加和删除100个元素会更改HashSet和重新构造元素的内部容量.这是完全有效的行为.我这里不是在问这个问题.

但是,使用JDK9,如果我这样做:

Set<String> set = Set.of("just", "a", "test");
System.out.println(set);
Run Code Online (Sandbox Code Playgroud)

然后,在JVM的另一个实例中,我运行相同的代码,输出可能不同,因为已经引入了随机化.

到目前为止,我在youtube上发现了这个优秀的视频(分钟44:55),其中Stuart Marks说这种随机化的一个动机是:

(...)人们编写的应用程序无意中依赖于迭代顺序.(...)所以,无论如何,迭代顺序是一个大问题,我认为有很多代码存在潜在的依赖于迭代顺序尚未发现的代码.(......)所以,我们对此的回应是故意在随机迭代顺序Set,并Map在新的集合.因此,在集合的迭代顺序不可预测但稳定之前,这些是可预测的不可预测的.因此,每次JVM启动时,我们都会获得一个随机数,并将其作为种子值使用,并与哈希值混合使用.因此,如果你运行一个初始化一个集合然后以任何顺序打印出元素的程序,你会得到一个答案,然后,如果再次调用JVM并运行相同的程序,那么元素集通常会出现在不同的顺序.所以,这里的想法是(...)如果你的代码中存在迭代顺序依赖,过去曾经发生的事情是,新的JDK版本出来了,你测试你的代码和(...)它' d需要数小时的调试才能将其追溯到迭代顺序中的某种变化.这意味着该代码中存在一个依赖于迭代顺序的错误.现在,如果你更频繁地改变迭代次序,比如每次JVM调用,那么(我们希望)奇怪的行为会更频繁地表现出来,事实上我们希望你在做测试时......

因此,动机很明确,而且很明显,这种随机化只会影响新的不可变集和映射.

我的问题是:这种随机化还有其他动机吗?它有什么优势?

java random collections maps java-9

12
推荐指数
2
解决办法
490
查看次数

方法链中的泛型类型参数推断

在阅读完这个问题之后,我开始考虑Java 8中的泛型方法.具体来说,当链接方法时,泛型类型参数会发生什么.

对于这个问题,我将使用Guava的一些通用方法ImmutableMap,但我的问题更为通用,可以应用于所有链式泛型方法.

考虑ImmutableMap.of具有此签名的泛型方法:

public static <K, V> ImmutableMap<K, V> of(K k1, V v1)
Run Code Online (Sandbox Code Playgroud)

如果我们使用这个泛型方法声明a Map,编译器会正确推断泛型类型:

Map<String, String> map = ImmutableMap.of("a", "b");
Run Code Online (Sandbox Code Playgroud)

我知道从Java 8开始,编译器推理机制已得到改进,即它从上下文中推断出通用类型的方法,在本例中是一个赋值.

上下文也可以是方法调用:

void someMethod(Map<String, String> map) { 
    // do something with map
}

someMethod(ImmutableMap.of("a", "b"));
Run Code Online (Sandbox Code Playgroud)

在这种情况下,泛型类型ImmutableMap.of是从参数的泛型类型推断出来的someMethod,即.Map<String, String> map.

但是当我尝试使用ImmutableMap.builder()和链接方法来构建我的地图时,我得到一个编译错误:

Map<String, String> map = ImmutableMap.builder()
    .put("a", "b")
    .build(); // error here: does not compile
Run Code Online (Sandbox Code Playgroud)

错误是:

Error:(...) java: incompatible types: 
ImmutableMap<Object, Object> …
Run Code Online (Sandbox Code Playgroud)

java generics methods method-chaining java-8

10
推荐指数
1
解决办法
611
查看次数

遇到订单友好/不友好的终端操作与并行/顺序与有序/无序流

这个问题的启发,我开始玩有序与无序流,并行与顺序流和终端操作,这些操作尊重遭遇顺序与不尊重它的终端操作.

在对链接问题的一个答案中,显示了与此类似的代码:

List<Integer> ordered = Arrays.asList(
    1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4);
List<Integer> result = new CopyOnWriteArrayList<>();

ordered.parallelStream().forEach(result::add);

System.out.println(ordered);
System.out.println(result);
Run Code Online (Sandbox Code Playgroud)

这些名单确实不同.该unordered列表甚至从一次运行变为另一次运行,表明结果实际上是非确定性的.

所以我创建了另一个例子:

CopyOnWriteArrayList<Integer> result2 = ordered.parallelStream()
        .unordered()
        .collect(Collectors.toCollection(CopyOnWriteArrayList::new));

System.out.println(ordered);
System.out.println(result2);
Run Code Online (Sandbox Code Playgroud)

我希望看到类似的结果,因为流是并行和无序的(可能unordered()是多余的,因为它已经是并行的).但是,生成的列表是有序的,即它等于源列表.

所以我的问题是为什么收集的清单是有序的?是否collect总是尊重遭遇顺序,即使对于并行,无序的流?它Collectors.toCollection(...)是强制遭遇秩序的特定收集者吗?

java java-8 java-stream

8
推荐指数
1
解决办法
128
查看次数

长流过滤

我有两个 Java LongStreams,我想从另一个流中删除一个流中存在的值。

LongStream stream 1 = ...
LongStream stream 2 = ...

stream2  = stream2.filter(e-> stream1.contains(e));
Run Code Online (Sandbox Code Playgroud)

LongStream 没有 contains 方法,在这种情况下我不知道如何使用 anyMatch,因为要检查的值来自另一个流,而不是变量或常量。

java java-stream

5
推荐指数
1
解决办法
51
查看次数