是否可以在Stream中获取下一个元素?

时间:2015-07-27 10:00:18

标签: java java-8

我正在尝试将for循环转换为功能代码。我需要向前看一个值,并且还要看一个值。是否可以使用流? 以下代码是将罗马文本转换为数值。 不确定带有两个/三个参数的reduce方法是否有帮助。

     <?xml version="1.0" encoding="UTF-8"?>
  <Root version="1">
<Application>
    <Name>redirect</Name>
    <AppType>VOD</AppType>
    <Description></Description>
    <!-- Uncomment to set application level timeout values
    <ApplicationTimeout>60000</ApplicationTimeout>
    <PingTimeout>12000</PingTimeout>
    <ValidationFrequency>8000</ValidationFrequency>
    <MaximumPendingWriteBytes>0</MaximumPendingWriteBytes>
    <MaximumSetBufferTime>60000</MaximumSetBufferTime>
    <MaximumStorageDirDepth>25</MaximumStorageDirDepth>
    -->
    <Connections>
        <AutoAccept>true</AutoAccept>
        <AllowDomains></AllowDomains>
    </Connections>
    <!--
        StorageDir path variables

        ${com.wowza.wms.AppHome} - Application home directory
        ${com.wowza.wms.ConfigHome} - Configuration home directory
        ${com.wowza.wms.context.VHost} - Virtual host name
        ${com.wowza.wms.context.VHostConfigHome} - Virtual host home directory
        ${com.wowza.wms.context.Application} - Application name
        ${com.wowza.wms.context.ApplicationInstance} - Application instance name

    -->
    <Streams>
        <StreamType>default</StreamType>
        <StorageDir>${com.wowza.wms.context.VHostConfigHome}/content</StorageDir>
        <KeyDir>${com.wowza.wms.context.VHostConfigHome}/keys</KeyDir>
        <!-- LiveStreamPacketizers (separate with commas): cupertinostreamingpacketizer, smoothstreamingpacketizer, sanjosestreamingpacketizer, mpegdashstreamingpacketizer, cupertinostreamingrepeater, smoothstreamingrepeater, sanjosestreamingrepeater, mpegdashstreamingrepeater, dvrstreamingpacketizer, dvrstreamingrepeater -->
        <LiveStreamPacketizers></LiveStreamPacketizers>
        <!-- Properties defined here will override any properties defined in conf/Streams.xml for any streams types loaded by this application -->
        <Properties>
        </Properties>
    </Streams>
    <Transcoder>
        <!-- To turn on transcoder set to: transcoder -->
        <LiveStreamTranscoder></LiveStreamTranscoder>
        <!-- [templatename].xml or ${SourceStreamName}.xml -->
        <Templates>${SourceStreamName}.xml,transrate.xml</Templates>
        <ProfileDir>${com.wowza.wms.context.VHostConfigHome}/transcoder/profiles</ProfileDir>
        <TemplateDir>${com.wowza.wms.context.VHostConfigHome}/transcoder/templates</TemplateDir>
        <Properties>
        </Properties>
    </Transcoder>
    <DVR>
        <!-- As a single server or as an origin, use dvrstreamingpacketizer in LiveStreamPacketizers above -->
        <!-- Or, in an origin-edge configuration, edges use dvrstreamingrepeater in LiveStreamPacketizers above -->
        <!-- As an origin, also add dvrchunkstreaming to HTTPStreamers below -->
        <!-- If this is a dvrstreamingrepeater, define Application/Repeater/OriginURL to point back to the origin -->
        <!-- To turn on DVR recording set Recorders to dvrrecorder.  This works with dvrstreamingpacketizer  -->
        <Recorders></Recorders>
        <!-- As a single server or as an origin, set the Store to dvrfilestorage-->
        <!-- edges should have this empty -->
        <Store></Store>
        <!--  Window Duration is length of live DVR window in seconds.  0 means the window is never trimmed. -->
        <WindowDuration>0</WindowDuration>
        <!-- Storage Directory is top level location where dvr is stored.  e.g. c:/temp/dvr -->
        <StorageDir>${com.wowza.wms.context.VHostConfigHome}/dvr</StorageDir>
        <!-- valid ArchiveStrategy values are append, version, delete -->
        <ArchiveStrategy>append</ArchiveStrategy>
        <!-- Properties for DVR -->
        <Properties>
        </Properties>
    </DVR>
    <TimedText>
        <!-- VOD caption providers (separate with commas): vodcaptionprovidermp4_3gpp, vodcaptionproviderttml, vodcaptionproviderwebvtt,  vodcaptionprovidersrt, vodcaptionproviderscc -->
        <VODTimedTextProviders>vodcaptionprovidermp4_3gpp</VODTimedTextProviders>
        <!-- Properties for TimedText -->
        <Properties>
        </Properties>
    </TimedText>
    <!-- HTTPStreamers (separate with commas): cupertinostreaming, smoothstreaming, sanjosestreaming, mpegdashstreaming, dvrchunkstreaming -->
    <HTTPStreamers>sanjosestreaming, cupertinostreaming, smoothstreaming, mpegdashstreaming</HTTPStreamers>
    <MediaCache>
        <MediaCacheSourceList></MediaCacheSourceList>
    </MediaCache>
    <SharedObjects>
        <StorageDir>${com.wowza.wms.context.VHostConfigHome}/applications/${com.wowza.wms.context.Application}/sharedobjects/${com.wowza.wms.context.ApplicationInstance}</StorageDir>
    </SharedObjects>
    <Client>
        <IdleFrequency>-1</IdleFrequency>
        <Access>
            <StreamReadAccess>*</StreamReadAccess>
            <StreamWriteAccess></StreamWriteAccess>
            <StreamAudioSampleAccess></StreamAudioSampleAccess>
            <StreamVideoSampleAccess></StreamVideoSampleAccess>
            <SharedObjectReadAccess>*</SharedObjectReadAccess>
            <SharedObjectWriteAccess>*</SharedObjectWriteAccess>
        </Access>
    </Client>
    <RTP>
        <!-- RTP/Authentication/[type]Methods defined in Authentication.xml. Default setup includes; none, basic, digest -->
        <Authentication>
            <PublishMethod>block</PublishMethod>
            <PlayMethod>none</PlayMethod>
        </Authentication>
        <!-- RTP/AVSyncMethod. Valid values are: senderreport, systemclock, rtptimecode -->
        <AVSyncMethod>senderreport</AVSyncMethod>
        <MaxRTCPWaitTime>12000</MaxRTCPWaitTime>
        <IdleFrequency>75</IdleFrequency>
        <RTSPSessionTimeout>90000</RTSPSessionTimeout>
        <RTSPMaximumPendingWriteBytes>0</RTSPMaximumPendingWriteBytes>
        <RTSPBindIpAddress></RTSPBindIpAddress>
        <RTSPConnectionIpAddress>0.0.0.0</RTSPConnectionIpAddress>
        <RTSPOriginIpAddress>127.0.0.1</RTSPOriginIpAddress>
        <IncomingDatagramPortRanges>*</IncomingDatagramPortRanges>
        <!-- Properties defined here will override any properties defined in conf/RTP.xml for any depacketizers loaded by this application -->
        <Properties>
        </Properties>
    </RTP>
    <MediaCaster>
        <RTP>
            <RTSP>
                <!-- udp, interleave -->
                <RTPTransportMode>interleave</RTPTransportMode>
            </RTSP>
        </RTP>
        <StreamValidator>
            <Enable>true</Enable>
            <ResetNameGroups>true</ResetNameGroups>
            <StreamStartTimeout>20000</StreamStartTimeout>
            <StreamTimeout>12000</StreamTimeout>
            <VideoStartTimeout>0</VideoStartTimeout>
            <VideoTimeout>0</VideoTimeout>
            <AudioStartTimeout>0</AudioStartTimeout>
            <AudioTimeout>0</AudioTimeout>
            <VideoTCToleranceEnable>false</VideoTCToleranceEnable>
            <VideoTCPosTolerance>3000</VideoTCPosTolerance>
            <VideoTCNegTolerance>-500</VideoTCNegTolerance>
            <AudioTCToleranceEnable>false</AudioTCToleranceEnable>
            <AudioTCPosTolerance>3000</AudioTCPosTolerance>
            <AudioTCNegTolerance>-500</AudioTCNegTolerance>
            <DataTCToleranceEnable>false</DataTCToleranceEnable>
            <DataTCPosTolerance>3000</DataTCPosTolerance>
            <DataTCNegTolerance>-500</DataTCNegTolerance>
            <AVSyncToleranceEnable>false</AVSyncToleranceEnable>
            <AVSyncTolerance>1500</AVSyncTolerance>
            <DebugLog>false</DebugLog>
        </StreamValidator>
        <!-- Properties defined here will override any properties defined in conf/MediaCasters.xml for any MediaCasters loaded by this applications -->
        <Properties>
        </Properties>
    </MediaCaster>
    <MediaReader>
        <!-- Properties defined here will override any properties defined in conf/MediaReaders.xml for any MediaReaders loaded by this applications -->
        <Properties>
        </Properties>
    </MediaReader>
    <MediaWriter>
        <!-- Properties defined here will override any properties defined in conf/MediaWriter.xml for any MediaWriter loaded by this applications -->
        <Properties>
        </Properties>
    </MediaWriter>
    <LiveStreamPacketizer>
        <!-- Properties defined here will override any properties defined in conf/LiveStreamPacketizers.xml for any LiveStreamPacketizers loaded by this applications -->
        <Properties>
        </Properties>
    </LiveStreamPacketizer>
    <HTTPStreamer>
        <!-- Properties defined here will override any properties defined in conf/HTTPStreamers.xml for any HTTPStreamer loaded by this applications -->
        <Properties>
        </Properties>
    </HTTPStreamer>
    <Manager>
        <!-- Properties defined are used by the Manager -->
        <Properties>
        </Properties>
    </Manager>
    <Repeater>
        <OriginURL></OriginURL>
        <QueryString><![CDATA[]]></QueryString>
    </Repeater>
    <StreamRecorder>
        <Properties>
        </Properties>
    </StreamRecorder>
    <Modules>
        <Module>
            <Name>base</Name>
            <Description>Base</Description>
            <Class>com.wowza.wms.module.ModuleCore</Class>
        </Module>
        <Module>
            <Name>logging</Name>
            <Description>Client Logging</Description>
            <Class>com.wowza.wms.module.ModuleClientLogging</Class>
        </Module>
        <Module>
            <Name>flvplayback</Name>
            <Description>FLVPlayback</Description>
            <Class>com.wowza.wms.module.ModuleFLVPlayback</Class>
        </Module>
        <Module>
            <Name>Redirect</Name>
            <Description>Redirect</Description>
            <Class>com.wowza.wms.plugin.loadbalancer.redirect.ClientConnections</Class>
        </Module>
    </Modules>
    <!-- Properties defined here will be added to the IApplication.getProperties() and IApplicationInstance.getProperties() collections -->
    <Properties>
    </Properties>
</Application>
   </Root>

4 个答案:

答案 0 :(得分:13)

不,这不可能使用流,至少不容易。流API远离处理元素的顺序:流可以并行处理,或者以相反的顺序处理。因此,流抽象中不存在“下一个元素”和“前一个元素”。

您应该使用最适合该作业的API:如果您需要对集合中的所有元素应用某些操作并且您对该订单不感兴趣,则流非常好。如果需要按特定顺序处理元素,则必须使用迭代器或者通过索引访问列表元素。

答案 1 :(得分:4)

我还没有看到这样的用例流,所以我不能说是否可能。但是当我需要使用带索引的流时,我选择IntStream#range(0, table.length),然后在lambdas中我从这个表/列表中获取值。

例如

    int[] arr = {1,2,3,4};
    int result = IntStream.range(0, arr.length)
            .map(idx->idx>0 ? arr[idx] + arr[idx-1]:arr[idx])
            .sum();

答案 2 :(得分:2)

根据流的性质,除非您阅读它,否则您不知道 next 元素。因此,在处理当前元素时,无法直接获取 next 元素。但是,由于您正在阅读当前元素,因此您需要知道之前读过的内容,因此要实现“访问以前的元素”和“访问 next 元素“,您可以依赖已经处理过的元素的历史记录。

以下两种解决方案可以解决您的问题:

  1. 访问以前读取的元素。通过这种方式,您可以了解当前元素和已定义的先前读取元素数
  2. 假设在流处理时您读取 next 元素,并且在上一次迭代中读取了当前元素。换句话说,您认为先前读取的元素为“当前”,当前处理的元素为 next (见下文)。
  3. 解决方案1 ​​ - 实施

    首先,我们需要一个数据结构,以便跟踪流经流的数据。好的选择可能是Queue的一个实例,因为队列本质上允许数据流过它们。我们只需要将队列绑定到我们想要知道的最后一个元素的数量(对于您的用例,这将是3个元素)。为此,我们创建一个“有界”的队列,保存这样的历史记录:

    public class StreamHistory<T> {
    
        private final int numberOfElementsToRemember;
        private LinkedList<T> queue = new LinkedList<T>(); // queue will store at most numberOfElementsToRemember
    
        public StreamHistory(int numberOfElementsToRemember) {
            this.numberOfElementsToRemember = numberOfElementsToRemember;
        }
    
        public StreamHistory save(T curElem) {
    
            if (queue.size() == numberOfElementsToRemember) {
                queue.pollLast(); // remove last to keep only requested number of elements
            }
    
            queue.offerFirst(curElem);
    
            return this;
        }
    
    
        public LinkedList<T> getLastElements() {
            return queue; // or return immutable copy or immutable view on the queue. Depends on what you want.
        }
    }
    

    泛型参数T是流的实际元素的类型。方法 save 返回对当前StreamHistory实例的引用,以便更好地与java Stream api集成(见下文),并不是真正需要它。

    现在唯一要做的就是将元素流转换为StreamHistory实例流(其中流的每个下一个元素将保存通过流的实际对象的最后 n 实例)。

    public class StreamHistoryTest {
      public static void main(String[] args) {
        Stream<Character> charactersStream = IntStream.range(97, 123).mapToObj(code -> (char) code); // original stream
    
        StreamHistory<Character> streamHistory = new StreamHistory<>(3); // instance of StreamHistory which will store last 3 elements
    
        charactersStream.map(character -> streamHistory.save(character)).forEach(history -> {
          history.getLastElements().forEach(System.out::print);
          System.out.println();
        });
    
      }
    
    }
    

    在上面的例子中,我们首先创建一个字母表中所有字母的流。然后我们创建StreamHistory的实例,它将被推送到原始流上的map()调用的每次迭代。通过调用map(),我们转换为包含对StreamHistory实例的引用的流。

    请注意,每次数据流经原始流时,对streamHistory.save(字符)的调用都会更新streamHistory对象的内容以反映流的当前状态。

    最后,在每次迭代中,我们打印最后3个已保存的字符。此方法的输出如下:

    a
    ba
    cba
    dcb
    edc
    fed
    gfe
    hgf
    ihg
    jih
    kji
    lkj
    mlk
    nml
    onm
    pon
    qpo
    rqp
    srq
    tsr
    uts
    vut
    wvu
    xwv
    yxw
    zyx
    

    解决方案2 - 实施

    虽然解决方案1在大多数情况下都可以完成工作并且相当容易理解,但是有一些用例可以检查下一个元素,之前的确非常方便。在这种情况下,我们只关注三个元素元组(pevious,current,next)并且只有一个元素无关紧要(简单的例子考虑下面的谜语:“给定一个数字流返回一个由三个后续数字组成的tupple,它给出了最高金额“)。要解决此类用例,我们可能希望拥有比StreamHistory类更方便的API。

    对于这种情况,我们引入了StreamHistory类的新变体(我们称之为StreamNeighbours)。该类将允许直接检查之前的 next 元素。处理将在时间“T-1”完成(即:当前处理的原始元素被视为 next 元素,之前处理的原始元素被认为是当前元件)。通过这种方式,我们可以提前检查一个元素。

    修改后的课程如下:

    public class StreamNeighbours<T> {
        private LinkedList<T> queue = new LinkedList(); // queue will store one element before current and one after
        private boolean threeElementsRead; // at least three items were added - only if we have three items we can inspect "next" and "previous" element
    
        /**
         * Allows to handle situation when only one element was read, so technically this instance of StreamNeighbours is not
         * yet ready to return next element
         */
        public boolean isFirst() {
            return queue.size() == 1;
        }
    
        /**
         * Allows to read first element in case less than tree elements were read, so technically this instance of StreamNeighbours is
         * not yet ready to return both next and previous element
         * @return
         */
        public T getFirst() {
            if (isFirst()) {
                return queue.getFirst();
            } else if (isSecond()) {
                return queue.get(1);
            } else {
                throw new IllegalStateException("Call to getFirst() only possible when one or two elements were added. Call to getCurrent() instead. To inspect the number of elements call to isFirst() or isSecond().");
            }
        }
    
        /**
         * Allows to handle situation when only two element were read, so technically this instance of StreamNeighbours is not
         * yet ready to return next element (because we always need 3 elements to have previos and next element)
         */
        public boolean isSecond() {
            return queue.size() == 2;
        }
    
        public T getSecond() {
            if (!isSecond()) {
                throw new IllegalStateException("Call to getSecond() only possible when one two elements were added. Call to getFirst() or getCurrent() instead.");
            }
            return queue.getFirst();
        }
    
    
        /**
         * Allows to check that this instance of StreamNeighbours is ready to return both next and previous element.
         * @return
         */
        public boolean areThreeElementsRead() {
            return threeElementsRead;
        }
    
    
        public StreamNeighbours<T> addNext(T nextElem) {
    
            if (queue.size() == 3) {
                queue.pollLast(); // remove last to keep only three
            }
    
            queue.offerFirst(nextElem);
    
            if (!areThreeElementsRead() && queue.size() == 3) {
                threeElementsRead = true;
            }
    
            return this;
        }
    
    
        public T getCurrent() {
            ensureReadyForReading();
            return queue.get(1); // current element is always in the middle when three elements were read
    
        }
    
        public T getPrevious() {
            if (!isFirst()) {
                return queue.getLast();
            } else {
                throw new IllegalStateException("Unable to read previous element of first element. Call to isFirst() to know if it first element or not.");
            }
        }
    
        public T getNext() {
            ensureReadyForReading();
            return queue.getFirst();
        }
    
        private void ensureReadyForReading() {
            if (!areThreeElementsRead()) { 
                throw new IllegalStateException("Queue is not threeElementsRead for reading (less than two elements were added). Call to areThreeElementsRead() to know if it's ok to call to getCurrent()");
            }
        }
    
    }
    

    现在,假设已经读取了三个元素,我们可以直接访问当前元素(这是在时间T-1通过流的元素),我们可以访问 next < / em>元素(这是当前通过流的元素)和 previous (这是在时间T-2通过流的元素):

    public class StreamTest {
      public static void main(String[] args) {
        Stream<Character> charactersStream = IntStream.range(97, 123).mapToObj(code -> (char) code);
    
        StreamNeighbours<Character> streamNeighbours = new StreamNeighbours<Character>();
    
    
        charactersStream.map(character -> streamNeighbours.addNext(character)).forEach(neighbours -> {
          //  NOTE: if you want to have access the values before instance of StreamNeighbours is ready to serve three elements
          //  you can use belows methods like isFirst() -> getFirst(), isSecond() -> getSecond()
          //
          //            if (curNeighbours.isFirst()) {
          //                Character currentChar = curNeighbours.getFirst();
          //                System.out.println("???" + " " + currentChar + " " + "???");
          //            } else if (curNeighbours.isSecond()) {
          //                Character currentChar = curNeighbours.getSecond();
          //                System.out.println(String.valueOf(curNeighbours.getFirst()) + " " + currentChar + " " + "???");
          //
          //            }
          //
          //   OTHERWISE: you are only interested in tupples consisting of three elements, so three elements needed to be read
    
          if (neighbours.areThreeElementsRead()) {
            System.out.println(neighbours.getPrevious() + " " + neighbours.getCurrent() + " " + neighbours.getNext());
          }
        });
    
      }
    
    }
    

    以下内容如下:

    a b c
    b c d
    c d e
    d e f
    e f g
    f g h
    g h i
    h i j
    i j k
    j k l
    k l m
    l m n
    m n o
    n o p
    o p q
    p q r
    q r s
    r s t
    s t u
    t u v
    u v w
    v w x
    w x y
    x y z
    

    通过StreamNeighbours类,更容易跟踪上一个/下一个元素(因为我们有适当名称的方法),而在StreamHistory类中这更麻烦,因为我们需要手动“反转”队列的顺序来实现这一点

答案 3 :(得分:0)

并非完全是Java解决方案,但是可以使用zip函数在Kotlin中获得下一个元素。这是我想出的功能。看起来更干净

enum class RomanNumeral(private val value: Int) {
    I(1), V(5), X(10), L(50), C(100), D(500), M(1000);

    operator fun minus(other: RomanNumeral): Int = value - other.value

    operator fun plus(num: Int): Int = num + value

    companion object {
        fun toRoman(ch: Char): RomanNumeral = valueOf(ch.toString())
    }
}

fun toNumber(roman: String): Int {
    return roman.map { RomanNumeral.toRoman(it) }
        .zipWithNext()
        .foldIndexed(0) { i, currentVal, (num1, num2) ->
            when {
                num1 < num2            -> num2 - num1 + currentVal
                i == roman.length - 2  -> num1 + (num2 + currentVal)
                else                   -> num1 + currentVal
            }
        }
}