如何交错(合并)两个Java 8流?

时间:2018-11-14 19:42:46

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

 Stream<String> a = Stream.of("one", "three", "five");
 Stream<String> b = Stream.of("two", "four", "six");

我要怎么做才能使输出显示如下?

// one
// two
// three
// four
// five
// six

我查看了concat,但正如javadoc解释的那样,它只是一个接一个地追加,它不会交错/散布。

Stream<String> out = Stream.concat(a, b);
out.forEach(System.out::println);
  

创建一个元素所有为        第一流的元素,然后是所有元素        第二流。

错误地给予

 // one
 // three
 // five
 // two
 // four
 // six

如果我收集它们并对其进行迭代,可以这样做,但是希望有更多Java8-y,Streamy:-)

注意

我不想压缩流

  

“ zip”操作将从每个集合中获取一个元素并将其组合。

zip操作的结果如下:(不需要)

 // onetwo
 // threefour
 // fivesix

6 个答案:

答案 0 :(得分:11)

我会使用类似这样的东西:

public static <T> Stream<T> interleave(Stream<T> a, Stream<T> b) {
    Spliterator<T> spA = a.spliterator(), spB = b.spliterator();
    long s = spA.estimateSize() + spB.estimateSize();
    if(s < 0) s = Long.MAX_VALUE;
    int ch = spA.characteristics() & spB.characteristics()
           & (Spliterator.NONNULL|Spliterator.SIZED);
    ch |= Spliterator.ORDERED;

    return StreamSupport.stream(new Spliterators.AbstractSpliterator<T>(s, ch) {
        Spliterator<T> sp1 = spA, sp2 = spB;

        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            Spliterator<T> sp = sp1;
            if(sp.tryAdvance(action)) {
                sp1 = sp2;
                sp2 = sp;
                return true;
            }
            return sp2.tryAdvance(action);
        }
    }, false);
}

它尽可能保留输入流的特征,从而可以进行某些优化(例如,count()toArray())。此外,即使输入流可能无序,它也会添加ORDERED来反映交织。

当一个流中的元素多于另一个时,其余元素将显示在末尾。

答案 1 :(得分:2)

比Holger笨拙的解决方案,但可能会满足您的要求:

<?php

class SMPP{
    var $debug = true;
    //SMPP bind parameters
    //var $system_type="WWW";
    var $interface_version=0x34;
    var $addr_ton=0;
    var $addr_npi=0;
    var $address_range="";
    //ESME transmitter parameters
    var $sms_service_type="";
    var $sms_source_addr_ton=0;
    var $sms_source_addr_npi=0;
    var $sms_dest_addr_ton=0;
    var $sms_dest_addr_npi=0;
    var $sms_esm_class=0;
    var $sms_protocol_id=0;
    var $sms_priority_flag=0;
    var $sms_schedule_delivery_time="";
    var $sms_validity_period="";
    var $sms_registered_delivery_flag=0;
    var $sms_replace_if_present_flag=0;
    var $sms_data_coding=0;
    var $sms_sm_default_msg_id=0;

    /**
     * Constructs the smpp class
     * @param $host - SMSC host name or host IP
     * @param $port - SMSC port
     */
    function SMPP($host, $port=5016){
        //internal parameters
        $this->sequence_number=1;
        $this->debug=false;
        $this->pdu_queue=array();
        $this->host=$host;
        $this->port=$port;
        $this->state="closed";
        //open the socket
        $this->socket=fsockopen($this->host, $this->port, $errno, $errstr, 30);
        if($this->socket)$this->state="open";
    }

    /**
     * Binds the receiver. One object can be bound only as receiver or only as trancmitter.
     * @param $login - ESME system_id
     * @param $port - ESME password
     * @return true when bind was successful
     */
    function bindTransceiver($login, $pass){
        if($this->state!="open")return false;
        if($this->debug){
            echo "Binding transciever...\n\n";
        }
        $status=$this->_bind($login, $pass, 0x00000009);
        if($this->debug){
            echo "Binding status  : $status\n\n";
        }
        if($status===0)$this->state="bind_trx";
        return ($status===0);
    }

    /**
     * Binds the receiver. One object can be bound only as receiver or only as trancmitter.
     * @param $login - ESME system_id
     * @param $port - ESME password
     * @return true when bind was successful
     */
    function bindReceiver($login, $pass){
        if($this->state!="open")return false;
        if($this->debug){
            echo "Binding receiver...\n\n";
        }
        $status=$this->_bind($login, $pass, 0x00000001);
        if($this->debug){
            echo "Binding status  : $status\n\n";
        }
        if($status===0)$this->state="bind_rx";
        return ($status===0);
    }
    /**
     * Binds the transmitter. One object can be bound only as receiver or only as trancmitter.
     * @param $login - ESME system_id
     * @param $port - ESME password
     * @return true when bind was successful
     */
    function bindTransmitter($login, $pass){
        if($this->state!="open")return false;
        if($this->debug){
            echo "Binding transmitter...\n\n";
        }
        $status=$this->_bind($login, $pass, 0x00000002);
        if($this->debug){
            echo "Binding status  : $status\n\n";
        }
        if($status===0)$this->state="bind_tx";
        return ($status===0);
    }
    /**
     * Closes the session on the SMSC server.
     */
    function close(){
        if($this->state=="closed")return;
        if($this->debug){
            echo "Unbinding...\n\n";
        }
        $status=$this->sendCommand(0x00000006,"");
        if($this->debug){
            echo "Unbind status   : $status\n\n";
        }
        fclose($this->socket);
        $this->state="closed";
    }

    /**
     * Read USSD response from SMSC.
     * @return 
     */
    function readResponse () {
        if($this->state!="bind_trx")return false;

        //set command id
        $command_id = 0x00000005;
    }
    /**
     * Read one SMS from SMSC. Can be executed only after bindReceiver() call. 
     * This method bloks. Method returns on socket timeout or enquire_link signal from SMSC.
     * @return sms associative array or false when reading failed or no more sms.
     */
    function readSMS(){
        if($this->state!="bind_rx")return false;
        //stream_set_timeout($this->socket, 10);
        $command_id=0x00000005;
        //check the queue
        for($i=0;$i<count($this->pdu_queue);$i++){
            $pdu=$this->pdu_queue[$i];
            if($pdu['id']==$command_id){
                //remove responce
                array_splice($this->pdu_queue, $i, 1);
                return parseSMS($pdu);
            }
        }
        //read pdu
        do{
            if($this->debug){
                echo "read sms...\n\n";
            }
            $pdu=$this->readPDU();
            //check for enquire link command
            if($pdu['id']==0x00000015){
                $this->sendPDU(0x80000015, "", $pdu['sn']);
                return false;
            }
            array_push($this->pdu_queue, $pdu);
        }while($pdu && $pdu['id']!=$command_id);
        if($pdu){
            array_pop($this->pdu_queue);
            return $this->parseSMS($pdu);
        }
        return false;
    }
    /**
     * Read one SMS from SMSC. Can be executed only after bindTransmitter() call.
     * @return true on succesfull send, false if error encountered
     */
    function sendSMS($from, $to, $message){
        if (strlen($from)>20 || strlen($to)>20)return false;
        if($this->state!="bind_tx")return false;
        $short_message = $message;
        //*
//      echo "test is just test ".$from;

        if(strlen($message) > 160){

            $length = 153;
            $max = 153;

            $msgs = array();

            array_push($msgs, substr($message, 0, $max));

            //echo $message." just me";
            while(true){

               if(strlen($message) - $length > $max){

                    array_push($msgs, substr($message, $length, $max));

                    $length = $length + $max;
            //echo "always here";
                    // msgs.add(message.substring(tempCount, max));
                    // tempCount += max;
                }else{
                    //echo "always here still";
                    array_push($msgs, substr($message, $length));

                   //$msgs.add(message.substring(tempCount));

                   break;
               }
            }
            //echo count($msgs)." count\n";
            if(count($msgs) > 0){

                for($i = 0; $i < count($msgs); $i++){
                    $short_message = $msgs[$i];
                    //*
                //  echo "\n".$short_message.": ".$i ;
                    $pdu = pack('a1cca'.(strlen($from)+1).'cca'.(strlen($to)+1).'ccca1a1ccccca'.(strlen($short_message)+1),
                        $this->sms_service_type,
                        $this->sms_source_addr_ton,
                        $this->sms_source_addr_npi,
                        $from,//source_addr
                        $this->sms_dest_addr_ton,
                        $this->sms_dest_addr_npi,
                        $to,//destination_addr
                        $this->sms_esm_class,
                        $this->sms_protocol_id,
                        $this->sms_priority_flag,
                        $this->sms_schedule_delivery_time,
                        $this->sms_validity_period,
                        $this->sms_registered_delivery_flag,
                        $this->sms_replace_if_present_flag,
                        $this->sms_data_coding,
                        $this->sms_sm_default_msg_id,
                        strlen($short_message),//sm_length
                        $short_message//short_message
                    );
                    $status=$this->sendCommand(0x00000004,$pdu);
                //  echo "\n".$status."final\n";
                }

                // return $status === 0;
            }



        }else{
            $short_message = $message;
            //*
            $pdu = pack('a1cca'.(strlen($from)+1).'cca'.(strlen($to)+1).'ccca1a1ccccca'.(strlen($message)+1),
                $this->sms_service_type,
                $this->sms_source_addr_ton,
                $this->sms_source_addr_npi,
                $from,//source_addr
                $this->sms_dest_addr_ton,
                $this->sms_dest_addr_npi,
                $to,//destination_addr
                $this->sms_esm_class,
                $this->sms_protocol_id,
                $this->sms_priority_flag,
                $this->sms_schedule_delivery_time,
                $this->sms_validity_period,
                $this->sms_registered_delivery_flag,
                $this->sms_replace_if_present_flag,
                $this->sms_data_coding,
                $this->sms_sm_default_msg_id,
                strlen($message),//sm_length
                $message//short_message
            );
            $status=$this->sendCommand(0x00000004,$pdu);
        }
        //*/
        // return $status;
        //echo $status."This is a test";
        return ($status == 0);
    }

    ////////////////private functions///////////////

    /**
     * @private function
     * Binds the socket and opens the session on SMSC
     * @param $login - ESME system_id
     * @param $port - ESME password
     * @return bind status or false on error
     */
    function _bind($login, $pass, $command_id){
        //make PDU
        $pdu = pack(
            'a'.(strlen($login)+1).
            'a'.(strlen($pass)+1).
            'a'.(strlen($this->system_type)+1).
            'CCCa'.(strlen($this->address_range)+1),
            $login, $pass, $this->system_type,
            $this->interface_version, $this->addr_ton,
            $this->addr_npi, $this->address_range);
        $status=$this->sendCommand($command_id,$pdu);
        return $status;
    }

    /**
     * @private function
     * Parse deliver PDU from SMSC.
     * @param $pdu - deliver PDU from SMSC.
     * @return parsed PDU as array.
     */
    function parseSMS($pdu){
        //check command id
        if($pdu['id']!=0x00000005)return false;
        //unpack PDU
        $ar=unpack("C*",$pdu['body']);
        $sms=array('service_type'=>$this->getString($ar,6),
            'source_addr_ton'=>array_shift($ar),
            'source_addr_npi'=>array_shift($ar),
            'source_addr'=>$this->getString($ar,21),
            'dest_addr_ton'=>array_shift($ar),
            'dest_addr_npi'=>array_shift($ar),
            'destination_addr'=>$this->getString($ar,21),
            'esm_class'=>array_shift($ar),
            'protocol_id'=>array_shift($ar),
            'priority_flag'=>array_shift($ar),
            'schedule_delivery_time'=>array_shift($ar),
            'validity_period'=>array_shift($ar),
            'registered_delivery'=>array_shift($ar),
            'replace_if_present_flag'=>array_shift($ar),
            'data_coding'=>array_shift($ar),
            'sm_default_msg_id'=>array_shift($ar),
            'sm_length'=>array_shift($ar),
            'short_message'=>$this->getString($ar,255)
        );
        if($this->debug){
            echo "Delivered sms:\n";
            print_r($sms);
            echo "\n";
        }
        //send responce of recieving sms
        $this->sendPDU(0x80000005, "\0", $pdu['sn']);
        return $sms;
    }
    /**
     * @private function
     * Sends the PDU command to the SMSC and waits for responce.
     * @param $command_id - command ID
     * @param $pdu - PDU body
     * @return PDU status or false on error
     */
    function sendCommand($command_id, $pdu){
        if($this->state=="closed")return false;
        $this->sendPDU($command_id, $pdu, $this->sequence_number);
        $status=$this->readPDU_resp($this->sequence_number, $command_id);
        $this->sequence_number=$this->sequence_number+1;
        return $status;
    }
    /**
     * @private function
     * Prepares and sends PDU to SMSC.
     * @param $command_id - command ID
     * @param $pdu - PDU body
     * @param $seq_number - PDU sequence number
     */
    function sendPDU($command_id, $pdu, $seq_number){
        $length=strlen($pdu) + 16;
        $header=pack("NNNN", $length, $command_id, 0, $seq_number);
        if($this->debug){
            echo "Send PDU        : $length bytes\n";
            $this->printHex($header.$pdu);
            echo "command_id      : ".$command_id."\n";
            echo "sequence number : $seq_number\n\n";
        }
        fwrite($this->socket, $header.$pdu, $length);
    }
    /**
     * @private function
     * Waits for SMSC responce on specific PDU.
     * @param $seq_number - PDU sequence number
     * @param $command_id - PDU command ID
     * @return PDU status or false on error
     */
    function readPDU_resp($seq_number, $command_id){
        //create responce id
        $command_id=$command_id|0x80000000;
        //check queue
        for($i=0;$i<count($this->pdu_queue);$i++){
            $pdu=$this->pdu_queue[$i];
            if($pdu['sn']==$seq_number && $pdu['id']==$command_id){
                //remove responce
                array_splice($this->pdu_queue, $i, 1);
                return $pdu['status'];
            }
        }
        //read pdu
        do{
            $pdu=$this->readPDU();
            if($pdu)array_push($this->pdu_queue, $pdu);
        }while($pdu && ($pdu['sn']!=$seq_number || $pdu['id']!=$command_id));
        //remove responce from queue
        if($pdu){
            array_pop($this->pdu_queue);
            return $pdu['status'];
        }
        return false;
    }
    /**
     * @private function
     * Reads incoming PDU from SMSC.
     * @return readed PDU or false on error.
     */
    function readPDU(){
        //read PDU length
        $tmp=fread($this->socket, 4);
        if(!$tmp)return false;
        extract(unpack("Nlength", $tmp));
        //read PDU headers
        $tmp2=fread($this->socket, 12);
        if(!$tmp2)return false;
        extract(unpack("Ncommand_id/Ncommand_status/Nsequence_number", $tmp2));
        //read PDU body
        if($length-16>0){
            $body=fread($this->socket, $length-16);
            if(!$body)return false;
        }else{
            $body="";
        }
        if($this->debug){
            echo "Read PDU        : $length bytes\n";
            $this->printHex($tmp.$tmp2.$body);
            echo "body len        : " . strlen($body) . "\n";
            echo "Command id      : $command_id\n";
            echo "Command status  : $command_status\n";
            echo "sequence number : $sequence_number\n\n";
        }
        $pdu=array(
            'id'=>$command_id,
            'status'=>$command_status,
            'sn'=>$sequence_number,
            'body'=>$body);
        return $pdu;
    }
    /**
     * @private function
     * Reads C style zero padded string from the char array.
     * @param $ar - input array
     * @param $maxlen - maximum length to read.
     * @return readed string.
     */
    function getString(&$ar, $maxlen=255){
        $s="";
        $i=0;
        do{
            $c=array_shift($ar);
            if($c!=0)$s.=chr($c);
            $i++;
        }while($i<$maxlen && $c!=0);
        return $s;
    }
    /**
     * @private function
     * Prints the binary string as hex bytes.
     * @param $maxlen - maximum length to read.
     */
    function printHex($pdu){
        $ar=unpack("C*",$pdu);
        foreach($ar as $v){
            $s=dechex($v);
            if(strlen($s)<2)$s="0$s";
            print "$s ";
        }
        print "\n";
    }
}
?>

答案 2 :(得分:2)

从问题注释中可以看到,我使用zip进行了尝试:

Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");

Stream<String> out = interleave(a, b);


    public static <T> Stream<T> interleave(Stream<T> streamA, Stream<T> streamB) {
        return zip(streamA, streamB, (o1, o2) -> Stream.of(o1, o2)).flatMap(s -> s);
    }

    /**
    * https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip
    **/
    private static <A, B, C> Stream<C> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<A, B, C> zipper) {
        final Iterator<A> iteratorA = streamA.iterator();
        final Iterator<B> iteratorB = streamB.iterator();
        final Iterator<C> iteratorC = new Iterator<C>() {
            @Override
            public boolean hasNext() {
                return iteratorA.hasNext() && iteratorB.hasNext();
            }

            @Override
            public C next() {
                return zipper.apply(iteratorA.next(), iteratorB.next());
            }
        };
        final boolean parallel = streamA.isParallel() || streamB.isParallel();
        return iteratorToFiniteStream(iteratorC, parallel);
    }

    private static <T> Stream<T> iteratorToFiniteStream(Iterator<T> iterator, boolean parallel) {
        final Iterable<T> iterable = () -> iterator;
        return StreamSupport.stream(iterable.spliterator(), parallel);
    }

答案 3 :(得分:1)

这可能不是是一个很好的答案,因为
(1)它会收集到地图,我想您不想这样做,
(2)它不是完全无状态的,因为它使用AtomicIntegers。

之所以添加它,是因为
(1)可读且
(2)社区可以从中获得一个想法并尝试对其进行改进。

Stream<String> a = Stream.of("one", "three", "five");
Stream<String> b = Stream.of("two", "four", "six");

AtomicInteger i = new AtomicInteger(0);
AtomicInteger j = new AtomicInteger(1);

Stream.of(a.collect(Collectors.toMap(o -> i.addAndGet(2), Function.identity())),
        b.collect(Collectors.toMap(o -> j.addAndGet(2), Function.identity())))
        .flatMap(m -> m.entrySet().stream())
        .sorted(Comparator.comparing(Map.Entry::getKey))
        .forEach(e -> System.out.println(e.getValue())); // or collect

输出

one
two
three
four
five
six

@Holger的编辑

Stream.concat(a.map(o -> new AbstractMap.SimpleEntry<>(i.addAndGet(2), o)),
        b.map(o -> new AbstractMap.SimpleEntry<>(j.addAndGet(2), o)))
        .sorted(Map.Entry.comparingByKey())
        .forEach(e -> System.out.println(e.getValue())); // or collect

答案 4 :(得分:1)

使用Iterator

的一种解决方案
final Iterator<String> iterA = a.iterator();
final Iterator<String> iterB = b.iterator();

final Iterator<String> iter = new Iterator<String>() {
  private final AtomicInteger idx = new AtomicInteger();
  @Override
  public boolean hasNext() { 
    return iterA.hasNext() || iterB.hasNext();
  }
  @Override
  public String next() {
    return idx.getAndIncrement() % 2 == 0 && iterA.hasNext() ? iterA.next() : iterB.next();
  }
};

 // Create target Stream with StreamEx from: https://github.com/amaembo/streamex    
 StreamEx.of(iter).forEach(System.out::println);

 // Or Streams from Google Guava
 Streams.stream(iter).forEach(System.out::println);

或者仅由我提供的ASLR中的解决方案:

 AtomicInteger idx = new AtomicInteger();
 StreamEx.merge(a, b, (s1, s2) -> idx.getAndIncrement() % 2 == 0 ? Nth.FIRST : Nth.SECOND).forEach(Fn.println()); 

答案 5 :(得分:0)

使用番石榴的Streams.zipStream.flatMap

Stream<String> interleaved = Streams
        .zip(a, b, (x, y) -> Stream.of(x, y))
        .flatMap(Function.identity());

interleaved.forEach(System.out::println);

打印:

one
two
three
four
five
six