0
0

Javaでzipしてみた

Last updated at Posted at 2024-07-09

Javaでzipしてみた

KotlinだとふたつのListをzipでかんたんに噛み合わせることができる。Javaにもあったらいいのにな、とずっとおもっていた。
Java22で導入されたGathererを眺めていたら、なんとなくzipできそうな雰囲気があったので、試してみた。

Gatherer

Java22でjava.util.stream.Gathererが導入されました。まだ、previewですが。

windowSlidingをためす

Gatherers#windowSliding()をつかうと、zip的なことができそうですが、ふたつのStreamを噛み合わせることは、できません。

Kotlinで一個ずらして噛み合わせる、そんな感じのことはできる。

$ kotlinc
>>> val list = listOf(1, 2, 3, 4)
>>> list.zip(list.drop(1))
res1: = [(1, 2), (2, 3), (3, 4)]
  @Test
public void aa() {
  final var list = List.of(1,2,3,4);
  final var result = list.stream().gather(Gatherers.windowSliding(2)).toList();
  System.out.println(result);
}
// [[1, 2], [2, 3], [3, 4]]

zipを実装してみる

ふたつのStream、とはいけませんでしたが、StreamとIteratorであれば、Gatherer#ofSequential()を使って噛み合わせることができました。

package sample;

import java.util.Iterator;
import java.util.stream.Gatherer;

/**
 */
public class MyGatherers {
  private MyGatherers() {}

  /**
   * @param otherIterator
   * @param <A>
   * @param <B>
   * @return
   */
  public static <A, B> Gatherer<A, Iterator<B>, Pair<A, B>> zip(Iterator<B> otherIterator) {
    final Gatherer<A, Iterator<B>, Pair<A, B>> gatherer = Gatherer.ofSequential(
        //
        () -> otherIterator,
        //
        Gatherer.Integrator.ofGreedy((iterator, first, downstream) -> {
          final boolean hasNext = iterator.hasNext();
          final B second = hasNext ? otherIterator.next() : null;

          //
          return hasNext ? downstream.push(new Pair<>(first, second)) : false;
        }));

    return gatherer;
  }
}
package sample;

public record Pair<A, B>(
    A first,
    B second
) {}

test

たとえば、Kotlinのこのような処理。

fun main() {
  data class Person(val id: String, val name: String)

  val listA = (1..10).map{Math.random()}
  val result = listA.zip(listOf(Person("a", "Alice"), Person("b", "Bob"), Person("c", "Carol")))
  println(result)
}

// [(0.5695021953159504, Person(id=a, name=Alice)), 
//  (0.6565867616482192, Person(id=b, name=Bob)),
//  (0.6789263224602792, Person(id=c, name=Carol))]

これを、定義したMyGatherers#zip()を使うと、このように実装できます。

  @Test
  void zip_21() {
    record Person(String id, String name) {}

    final var result = IntStream.range(1, 10)
        .mapToDouble(it -> Math.random())
        .boxed()
        .gather(MyGatherers.zip(
            List.of(new Person("a", "Alice"), new Person("b", "Bob"), new Person("c", "Carol")).listIterator()))
        .toList();

    System.out.println(result);
    Assertions.assertEquals(3, result.size());
  }
// [Pair[first=0.0827916056379453, second=Person[id=a, name=Alice]],
//  Pair[first=0.2503321398502284, second=Person[id=b, name=Bob]],
//  Pair[first=0.1072768883956946, second=Person[id=c, name=Carol]]]

いい感じでつかえそうです。

package sample;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.stream.IntStream;

class MyGatherersTest {
  
  @Test
  void zip_01() {
    final var listA = List.of(1, 2, 3);
    final var listB = List.of("aa", "kk", "ss", "tt");

    final List<Pair<Integer, String>> result =
        listA.stream()
            .gather(MyGatherers.zip(listB.listIterator()))
            .toList();

    System.out.println(result);
    Assertions.assertEquals(3, result.size());
  }


  @Test
  void zip_02() {
    final List<Integer> listA = List.of();
    final var listB = List.of("aa", "kk", "ss", "tt");

    final List<Pair<Integer, String>> result =
        listA.stream()
            .gather(MyGatherers.zip(listB.listIterator()))
            .toList();

    System.out.println(result);
    Assertions.assertTrue(result.isEmpty());
  }

  @Test
  void zip_03() {
    final var listA = List.of(1, 2, 3);
    final List<String> listB = List.of();

    final List<Pair<Integer, String>> result =
        listA.stream()
            .gather(MyGatherers.zip(listB.listIterator()))
            .toList();

    System.out.println(result);
    Assertions.assertTrue(result.isEmpty());
  }

  @Test
  void zip_21() {
    record Person(String id, String name) {
    }

    final var result = IntStream.range(1, 10)
        .mapToDouble(it -> Math.random())
        .boxed()
        .gather(MyGatherers.zip(
            List.of(new Person("a", "Alice"), new Person("b", "Bob"), new Person("c", "Carol")).listIterator()))
        .toList();

    System.out.println(result);
    Assertions.assertEquals(3, result.size());
  }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0