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
}
}
}
我认为这是不可能的,反正是个有趣的问题。@sidgateString[]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
}
}
}