Java 是否可以获取流中的下一个元素?

Java 是否可以获取流中的下一个元素?,java,java-8,functional-programming,java-stream,Java,Java 8,Functional Programming,Java Stream,我正在尝试将循环的转换为函数代码。我需要向前看一个值,也要向后看一个值。可以使用流吗? 以下代码用于将罗马文本转换为数值。 不确定带两个/三个参数的reduce方法是否有帮助 int previousCharValue = 0; int total = 0; for (int i = 0; i < input.length(); i++) { char current = input.charAt(i); RomanNumeral romanNu

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

int previousCharValue = 0;
int total = 0;
    
for (int i = 0; i < input.length(); i++) {
    char current = input.charAt(i);
        
    RomanNumeral romanNum = RomanNumeral.valueOf(Character.toString(current));
        
    if (previousCharValue > 0) { 
        total += (romanNum.getNumericValue() - previousCharValue);
        previousCharValue = 0;
    } else {
        if (i < input.length() - 1) {
        
            char next = input.charAt(i + 1);
            RomanNumeral nextNum = RomanNumeral.valueOf(Character.toString(next));
            if (romanNum.getNumericValue() < nextNum.getNumericValue()) {
                previousCharValue = romanNum.getNumericValue();
            }
        }
        if (previousCharValue == 0) {
            total += romanNum.getNumericValue();
        }
            
    }
        
}
int-previousCharValue=0;
int-total=0;
对于(int i=0;i0){
总计+=(romanNum.getNumericValue()-previousCharValue);
previousCharValue=0;
}否则{
if(i
我还没有看到流的这种用例,所以我不能说它是否可行。但是当我需要使用带索引的流时,我选择
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();

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


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

根据流的性质,您不知道下一个元素,除非您阅读它。因此,在处理当前元素时,无法直接获取下一个元素。然而,由于您正在读取当前元素,您显然知道之前读取了什么,因此为了实现“访问上一个元素”和“访问下一个元素”这样的目标,您可以依赖已处理的元素的历史记录

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

  • 获取对以前读取的元素的访问权限。通过这种方式,您可以知道当前元素和以前读取的元素的定义数量
  • 假设在流处理的那个时刻,您读取了下一个元素,而当前元素是在上一次迭代中读取的。换句话说,您将先前读取的元素视为“当前”,并将当前处理的元素作为下一个(参见下面)。 解决方案1-实施

    首先,我们需要一个数据结构,它允许跟踪流经流的数据。好的选择可能是一个很好的例子,因为队列本质上允许数据在其中流动。我们只需要将队列绑定到我们想要知道的最后元素的数量(对于您的用例,这将是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.
        }
    }
    
    解决方案2-实施

    虽然解决方案1在大多数情况下都能完成这项工作,而且很容易遵循,但有一些用例可以检查下一个元素,而上一个元素确实很方便。在这种情况下,我们只对三个元素元组感兴趣(PeViod,Actudio,NeXT),并且只有一个元素不重要(简单的例子考虑下面的谜语:“给定一个数字流返回三个后续数字的倍数,给出最高的总和))。为了解决此类用例,我们可能希望使用比StreamHistory类更方便的api

    对于这个场景,我们引入了StreamHistory类的一个新变体(我们称之为StreamNeights)。该类将允许直接检查上一个和下一个元素。处理将在时间“T-1”内完成(即:当前处理的原始元素被视为下一个元素,先前处理的原始元素被视为当前元素)。这样,在某种意义上,我们可以检查前面的一个元素

    修改的类如下所示:

    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
    
    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()");
            }
        }
    
    }
    
    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
    

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

    不完全是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
                }
            }
    }
    
    enum类罗马数字(私有值:Int){
    I(1)、V(5)、X(10)、L(50)、C(100)、D(500)、M(1000);
    运算符fun减号(其他:罗马数字):Int=value-other.value
    运算符fun plus(num:Int):Int=num+value
    伴星{
    fun-toRoman(ch:Char):罗马数字=valueOf(ch.toString())
    }
    }
    fun toNumber(罗马:字符串):Int{
    return roman.map{romannumeric.toRoman(it)}
    .zipWithNext()
    .foldIndexed(0){i,currentVal,(num1,num2)->
    什么时候{
    num1num2-num1+currentVal
    i==roman.length-2->num1+(num2+currentVal)
    else->num1+currentVal
    }
    }
    }
    
    我认为这是不可能的,反正是个有趣的问题。@sidgate
    String[]characters=input.split(“”);IntStream.range(0,characters.length).
    这行代码中的一些内容,但不是在java-8机器上尝试。这真是扭曲的代码。这很难理解,我仍然不确定它是否起作用
    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
    
    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
                }
            }
    }