Collections BiPredicate in Java 8 Streams

reemstar

Mitglied
Hallo,

ich versuche gerade Werte einer Liste mit hilfe von Java 8 Streams zu vergleichen. Dabei möchte ich feststellen ob z.B. alle Elemente der Liste Instancen der gleichen Klasse sind, bzw. genauer den gleichen Laufzeittyp haben. (ist eigenlich nicht so wichtig..🙂) Dabei gehe ich folgendermaßen vor:

1. Definieren eines BiPredicate:
Java:
	BiPredicate<Expression, Expression> typesNotEqual = (Expression exp1, Expression exp2)-> ts
				.typeof(exp1, trace) != ts.typeof(exp2, trace);

2. Verwenden des Prädikates in einem Stream, ein einer If-Bedingung
Java:
..
if (sequenceExpressionList.getElement()	
.stream()
.anyMatch(typesNotEqual.test(sequenceExpressionList.getElement().stream().findFirst().get(), sequenceExpressionList.getElement().stream().findAny().get())))
..

Leider bringt mir der Compiler folgenden Syntaxfehler:
The method anyMatch(Predicate<? super Expression>) in the type Stream<Expression> is not applicable for the arguments (boolean)

Das verstehe ich jedoch nicht ganz, da das Prädicat ja einen boolschen Wert zurückliefert.

Evtl. hat jemand eine Idee oder so. Oder vielleicht ein Bsp. wie ich ein BiPredicate sinnvoll einsetze.

Gruß,
reemstar

PS: (Ich will es gerne mit der Streams-API lösen, und nicht extra alle Werte über eine for-loop händisch anfassen)
 
test liefert einen boolschen Wert zurück, anyMatch will aber ein Predicate und keinen boolean.

Versuch mal das hier
Java:
Stream<Expression> stream = sequenceExpressionList.getElement().stream();

boolean sameClass = stream.findFirst().map(exp1 -> stream.allMatch(exp2 ->
    ts.typeof(exp1, trace) == ts.typeof(exp2, trace))).orElse(true);
 
Danke für die schnelle Antwort!

Die Streams-API kann echt böse sein! Wenn innerhalb eines steams eine Expception fliegt kann er vorzeitig beendet werden (!) und wenn ich deinen Vorschlag so umsetze, wird wahrscheinlich der "äußere" stream beendet bevor der innere durchgelaufen ist oder so.
Dies passiert NUR wenn ich den stream seperat vorher anlege:

Java:
Stream<Expression> stream = sequenceExpressionList.getElement().stream();


Wenn ich die Abfrage dann checken möchte, z.B. in einer If-clause, bricht mir der stream einfach ab
und die clause wird nicht ausgeführt! Ohne jegliche Exception!!


Hier mein Test:
Java:
	if ( stream .findFirst().map(exp1 -> stream .allMatch(exp2 ->  isSameType(exp1, typeof(exp1, trace), exp2, typeof(exp2, trace), trace))).orElse(true)) {
			System.out.println("########### TYPES  EQUAL ###########");
		} else {
			System.out.println("########### TYPES NOT EQUAL ###########"); 
		}

Wenn ich den stream jedoch jedes mal für den inneren und äußeren Durchlauf neu öffne, funktioniert das ganze.

Also so:
Java:
//stream wird jeds mal neue geöffnet
if ( sequenceExpressionList.getElement().stream().findFirst().map(exp1 ->  sequenceExpressionList.getElement().stream().allMatch(exp2 ->  isSameType(exp1, typeof(exp1, trace), exp2, typeof(exp2, trace), trace))).orElse(true)) {
			System.out.println("########### TYPES  EQUAL ###########");
		} else {
			System.out.println("########### TYPES NOT EQUAL ###########"); 
		}

Das ist mal wirklich ein sehr (!) unvorhersehbares Verhalten.

Vielen Dank nochmals für den Tipp!
 
[...]
Das ist mal wirklich ein sehr (!) unvorhersehbares Verhalten. [...]

Nein, es ist sogar dokumentiert, ebenso das eine unbehandelte Exception die streamverarbeitende Pipeline beendet. "Terminierende"-Operationen auf einem Stream, also alle Operationen die nicht wieder den Stream zurückliefern, schließen den Stream.
Deshalb können Streams auch nicht rekursiv verwendet werden, sie sind einfach nicht "lazy" im Sinne anderer funktionaler Programmiersprachen. Normalerweise fliegt eine Exception wenn man den Stream derart verwendet.

Hier ein stumpfes konstruiertes Beispiel:

Java:
import java.util.stream.Stream;

public class StreamException {
    public static void main(String...args){
        Stream<Integer> integerStream = Stream.of(1,2,3,4,5,6);
        integerStream.allMatch(i -> {integerStream.map(a -> a); return true;});
    }
}

Erzeugt normalerweise eine Exception (gekürzt) folgender Art. Wundert mich das du nicht so etwas ähnliches siehst.

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$3.<init>(ReferencePipeline.java:187)
 
Hallo,

danke für den Hinweis! Wenn ich mein/dein Bsp. im standalone Fall betrachte, wird die Exception geworfen. Wenn die Methode, in der die Abfrage stattfindet jedoch aus einer Eclipse-Laufzeitinstanz aufgerufen wird, geht die Exception verloren..komisch.

Jetzt bin ich doch etwas verunsichert, denkst du der code ist nun sauber und in allen Fällen valide, wenn ich den stream jedes mal neue öffne?
 
[...]
Jetzt bin ich doch etwas verunsichert, denkst du der code ist nun sauber und in allen Fällen valide, wenn ich den stream jedes mal neue öffne?

Streams erzeugen sollte relativ kostengünstig sein (zumindest hoffe ich das bei der Limitierung innerhalb Javas). Der Code sieht aber so auch ein wenig unleserlich aus, was ich dir empfehlen würde ist das du nicht zwanghaft versuchst Lösungen mithilfe von Streams zu erreichen wenn es
anders vll. wartbarer funktioniert.

Persönlich wäre ich da wohl etwas anders vorgegangen, ich würde versuchen die Streams zu kombinieren und auf ihre Typen abzubilden. Dann greife ich mir das erste Element aus einem der Urstreams und vergleiche das mit den Elementen des kombinierten Streams. Das macht es zumindest etwas leserlicher. Natürlich muss man noch irgendwie mit leeren Streams umgehen.

Ein konstruiertes Beispiel:

Java:
import java.util.List;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

public class StreamException {
    public static void main(String...args){

        List<Object> integerList = Stream.of(1,2,3,4,5,6).collect(toList());
        List<Object> stringList  = Stream.of("a", "b", "c,", "d").collect(toList());

        Object foo = integerList
                        .stream()
                        .findFirst().get();

        boolean allMatch =
                Stream
                    .concat(integerList.stream().map(i -> i.getClass()),
                            stringList.stream().map(s -> s.getClass()))
                    .allMatch(c -> foo.getClass().equals(c));
    }
}
 
Kleiner Nachtrag, ich hatte gestern wohl überlesen das es sich bei dir nur um eine einzige Liste handelt, die aus gemischten Typen besteht. Die Idee ist die selbe, wenn alle Elemente den selben Typen haben, dann wird mir allMatch-true zurückgeben wenn ich ein beliebiges Element aus der Liste nehme und dessen Typ mit allen anderen Typen der vorhandenen Elemente abgleiche.

Dann würde sich der obige Code zusammenkürzen auf Folgendes:

Java:
public class StreamClass {
    public static void main(String...args){
        List<Object> mixedList  = Stream.of(1,"a", 2, "b", 3, "c,", 4,"d").collect(Collectors.toList());

        Class<?> clazz = mixedList
                .stream()
                .findFirst().get().getClass();

        boolean allMatch = mixedList.stream().allMatch(c -> clazz.equals(c.getClass()));
    }
}
 
Hallo,

danke nochmals für die hilfreichen Tipps! Bei der schlechten Lerserlichkeit des codes muss ich dir natürlich zustimmen. Aber mit Hilfe der Streams kann ich die anyMatch/allMatch Funktionen so gut anwenden um Ausdrücke der Form:

Java:
 meine var:Integer = new Interger[]{1,2,11,22, "hans", new Person()};

bzgl. ihrer Typen zu validieren.
 

Zurück
Oben