使用stream从String []中创建String [] [],索引作为值

时间:2017-10-25 15:39:24

标签: java arrays java-stream

我有一个String[],其值必须放入String[][],每个“行”由当前索引和前一个数组的值组成。迭代解决方案如下所示:

String[] vals = getValsFromSomewhere();
String[][] targetArray = new String[vals.length][];
for (int i = 0; i < targetArray.length; i++) {
    targetArray[i] = new String[]{
        Integer.toString(i), vals[i]
    };
}

我目前尝试将其更改为流以避免临时变量并在我的方法中摆脱for循环,但到目前为止我提出的最佳解决方案是:

String[] vals = getValsFromSomewhere();
String[][] targetArray = IntStream.range(0, vals.length)
    .mapToObj(index -> new String[]{
        Integer.toString(index), vals[index]
     })
    .toArray(String[][]::new);

这是一个有流的解决方案,好吧。但是,我的原始目标的一部分,即摆脱临时变量(在这种情况下为vals),是没有实现的。搜索Stream中的可用方法并没有提出任何看起来有用的东西,但我可能错过了一些东西。这就是为什么我在这里问;-)指出我正确的方向是非常感谢。

2 个答案:

答案 0 :(得分:0)

删除临时变量 vals 意味着您无法在创建Stream之前知道要处理的元素数量,因此无法使用 range() 。我无法看到如何消除所有临时变量,但作为替代方案,我引入了一个 AtomicInteger ,它在流处理中递增以支持索引。

我当然不会声称这是一种比你更好的方法,如果除了 range()之外还有其他任何方法可以使用流进行索引,我感兴趣 AtomicInteger (仅使用标准Java)。

以下是代码:

String[] getValsFromSomewhere() {

    return new String[]{"The", "rain", "in", "Spain", "stays", "mainly", "in", "the", "plain!"};
}

void test() {

    AtomicInteger ai = new AtomicInteger(0);

    String[][] target = Stream.of(getValsFromSomewhere())
                              .map(e -> new String[]{e, String.valueOf(ai.getAndIncrement())})
                              .toArray(String[][]::new);

    for(int row=0; row<target.length; row++) {
        for(int element=0; element<target[row].length; element++) {
            System.out.println("target[" + row +"] [" + element + "] = " + target[row][element]);
        }
    }    
}

答案 1 :(得分:0)

为什么不写自己的Collector(或者更好的说让我给你写一个)?似乎可能有点矫枉过正,但这可以使用:

class Array2DCollector implements Collector<String, Map<Integer,String>, String[][]>{

    private final AtomicInteger index = new AtomicInteger(0);

    @Override
    public Supplier<Map<Integer, String>> supplier(){
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<Integer, String>, String> accumulator(){
        return (map, string) -> map.put(index.getAndIncrement(), string);
    }

    @Override
    public BinaryOperator<Map<Integer, String>> combiner(){
        return (map1, map2) -> {
            map1.putAll(map2);
            return map1;
        };
    }

    @Override
    public Function<Map<Integer, String>, String[][]> finisher(){
        return map -> {
            final String[][] array = new String[index.get()][2];
            for(int i = 0; i < map.size(); i++){
                array[i][0] = String.valueOf(i);
                array[i][1] = map.get(i);
            }
            return array;
        };
    }

    @Override
    public Set<Characteristics> characteristics(){
        return EnumSet.noneOf(Characteristics.class);
    }
}

然后可以很容易地使用如下

final String[][] target = Arrays.stream(vals).collect(new Array2DCollector());

说明:简而言之,收集器有一个内部变量index,仅用于计数。对于流中的每个元素,此变量都会增加,并且元素将保存在hashMap中。最后,hashmap被转换为2D-Array。

重要提示:此收集器基于HashMap,无法执行并发已订购

最后注意事项:如上所述,这有点矫枉过正,但为什么不尝试一下;)