Wie erhalte ich das umgekehrte Resultat für einen Java Streams Filter?

Java Streams sind eine feine Sache. Ich habe das Gefühl, ich fange mittlerweile alle meine Java Beiträge mit diesem Satz an. 🙂

Ein Java Streams Filter erlaubt es einem, auf einfache Weise alle gewünschten Einträge aus einer Liste herauszufiltern und den Rest wegzuschmeissen. Alle roten Elefanten, alle Kinder unter 6 Jahren, alle Schlangen mit Flügeln und so weiter.

Oftmals ist es aber einfacher zu sagen, was man nicht will. Als quasi ‚Schau her, ich selektiere alle Kinder zwischen 5 und 8 Jahren… und jetzt, du superkluges Java, gib mir alle anderen Kinder!

Wie macht man das?

Not Predicate definieren

Ganz einfach: Wo auch immer man den Filter umgekehren will definiert man ein Not Predicate. Predicate ist ja dieses Dings in dem Filter, und wenn das „true“ ist, wird der jeweilige Eintrag behalten. Und mit einem Not Predicate drehen wir die Logik einfach um.

Folgenden Code irgendwo hinschmeissen:

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

Und dann kann man das not im Filter verwenden.

Beispiel: Ihr habt eine Liste mit Kindernamen und findet den Namen „Rupert“ einfach wiederlich, den wollt Ihr unbedingt losweren. Also definieren wir zuerst einen Filter für den Namen ‚Rupert‘:

Arrays.stream(kindernamen).filter(kindername -> kindername.contains("Rupert")).collect(Collectors.toList());

Dieser Filter würde jetzt also alle Ruperts in der Resultateliste haben.

Dann kehren wir den Filter einfach um:

Arrays.stream(kindernamen).filter(not(kindername -> kindername.contains("Rupert"))).collect(Collectors.toList());

Et voilà.

Java 11 hats schon drin

Kleine Randbemerkung: In Java 11 ist genau diese Methode beim Predicate schon dabei:

Predicate.not( … )

Siehe https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/Predicate.html#not(java.util.function.Predicate)

Dann geht unser Rupert-Hass-Filter folgendermassen (und das Predicate muss man natürlich nicht mehr selber definieren):

Arrays.stream(kindernamen).filter(Predicate.not(kindername -> kindername.contains("Rupert"))).collect(Collectors.toList());
Werbeanzeigen

Wie verwende ich Java Streams bei zwei For Loops?

Streams sind wirklich eine feine Sache. So fein, dass ich heutzutage praktisch keine For-Loops mehr sehe. Streams sind auch tatsächlich einfach und am Ende sauschnell, um die Daten zu verarbeiten.

Aber wie verwende ich Streams, wenn ich zwei For Loops benötigen würde für die Verarbeitung? Also wenn ich eine „Liste in einer anderen Liste“ habe?

Die kurze Antwort ist:

  • Wenn man beim Verarbeiten der inneren Liste auf die Objekte der äusseren Liste zugreifen will, verwendet man zwei Streams() mit AnyMatch.
  • Wenn man beim Verarbeiten der inneren Liste nicht auf die Objekte der äusseren Liste zugreifen will, kann man FlatMap verwenden.

FlatMap? Was ist das schon wieder für eine Teufelei?

Ja schrecklich, nicht? Da denkst Du, Du hättest endlich mal alles wissenswerte über Java gelernt, und dann fange ich an, von FlatMaps zu schwafeln.

Um FlatMaps zu erklären fange ich gleich an mit dem Beispiel, dass ich auch unten im Code zeigen werden:

Ich habe eine Liste von Müttern und jede Mutter hat wiederum eine Liste von Ihren Kindern. Meine Aufgabe ist es, die Mutter mit dem Kind „Paul“ zu finden.

Die Daten könnten also folgendermassen aussehen:

Äussere Liste:

  • Mutter Janet
  • Mutter Petra

Janet hat zwei Kinder:

  • John
  • Jessica

Petra hat zwei Kinder:

  • Patrick
  • Paul

Wenn ich jetzt eine FlatMap verwende, erstelle ich für die Mütter einen Stream und erhalte dann einfach alle Kinder aneinandergereiht statt „pro Mutter“

Also statt

Janet { John, Jessica } Petra { Patrick, Paul }

erhalte ich

{ John, Jessica, Patrick, Paul }

Wenn ich jetzt die Information der Mutter nicht benötige und nur die Attribute eines Kindes prüfen kann (Beispielsweise: „Ist das Kind älter als 6 Jahre?“) dann funktioniert eine FlatMap sehr gut.

Sobald ich aber die Information der dazugehörigen Mutter brauche, geht das nicht mehr (Beispiel: „Zu welcher Mutter gehört das Kind Paul?“). Dann kann ich zwei Streams verwenden (siehe Codebeispiel unten).

Okay ich bin bereit. Bring mir den Code!

Der Code zeigt zuerst, wie man für Paul auf herkömmliche Weise herausfindet, wer seine Mutter ist. Dann verwende ich zwei Streams mit anyMatch im inneren Stream. Und am Ende noch eine FlatMap, wobei ich aber fälschlicherweise am Ende das Kind erhalte statt die Mutter.

Die Klassen für Mutter und Kind sowie das setzen der Werte finden sich ganz unten in der Klasse.

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class FindPaul {

List<Mother> mothers = new ArrayList<>();

public void findPaul() {
	setupMothers();
	twoForLoops();
	twoStreams();
	flatMap();	
}

------------------------------------------------------------

private void twoForLoops() {
	for(Mother  mother: mothers) {
		for (Child child: mother.children) {
			if("Paul".equals(child.name)) {
				System.out.println("1: " + mother.name + " is the mother of Paul");
			}
		}
	}
}

------------------------------------------------------------

private void twoStreams() {
	Optional<Mother> paulsMother = mothers.stream().filter(
		mother -> mother.children.stream().anyMatch(child -> "Paul".equals(child.name))				
	).findFirst();				
	System.out.println("2: " + paulsMother.get().name + " is the mother of Paul");		
}

------------------------------------------------------------

// flatMap makes the conversion: 
// Stream<List<Object>>	-> flatMap ->	Stream<Object>
private void flatMap() {
	// Fehler: Mit Flatmap hat man keinen Bezug mehr auf das "Parent" Element
	// Man kriegt ein "Child", nicht die Mutter
	Optional<Child> paulsMother = mothers.stream().flatMap(mother -> mother.children.stream()).filter(child -> "Paul".equals(child.name)).findFirst();					
	System.out.println("3: " + paulsMother.get().name + " is Paul itself, and who is the mother?");
}

------------------------------------------------------------

private void setupMothers() {
	Mother m1 = new Mother("Janet");
	m1.children.add(new Child("John"));
	m1.children.add(new Child("Jessica"));
	
	Mother m2 = new Mother("Petra");
	m2.children.add(new Child("Patrick"));
	m2.children.add(new Child("Paul"));
	
	mothers.add(m1);
	mothers.add(m2);
}

class Child {
	String name;		
	Child(String name) { this.name = name; }
}

class Mother {
	String name;
	List<Child> children = new ArrayList<>();
	Mother(String name) { this.name = name; }
}	
}

Übrigens: Falls jemand eine noch bessere Methode hat, um zwei For Loops zu verarbeiten, bin ich jederzeit bereit, etwas dazuzulernen. 🙂

AnyMatch und AllMatch

Noch ein kurzer Nachtrag: Wenn man zwei For Loops durch Streams ersetzt und dabei die Lösung verwendet, bei der man in der inneren Schleife die äusseren Objekte kennen muss (oben im Code die Methode twoStreams()) dann muss man noch aufpassen, dass man tatsächlich das richtige herausfiltert.

Bei dem angegebenen Beispiel mit „AnyMatch“ muss im inneren Stream einfach ein Kind „Paul“ heissen.

Will man aber, dass alle Kinder „Paul“ heissen, muss man statt AnyMatch „AllMatch“ verwenden. Der Rest des Codes bleibt der gleiche.

Humans have optimized the fun out of League of Legends

Recently I have played League of Legends aka LOL again because – well – it is a fun game, isn’t it? Controlling your character, seeing cool graphics, having those big clashes of teams that make the screen explode. And in the end LOL is much more relaxed than Dota 2, right?

Yes, I had a short honeymoon phase, before I realized why I quitted LOL so many times before: Humans have optimized the game so hard that it’s no longer fun for a „casual“ player like myself to play it.

But what the hell do I mean with that?

Determined hero role3

Well to begin with: You can’t f***ing decide how you play the game.

So let’s talk again how you start a game of LOL:

  1. You press the button to start the game. Alright.
  2. You find a game
  3. You join the game
  4. And in the first 3 seconds every other player except you spams the chat with the role he/she wants to play.

So all you see is „jgl“, „top“, „mid“, „adc“.

What now?

Well, this means you are „forced“ to play support in the bottom lane. You are „forced“ to choose a support hero and support the ADC in the bottom lane – so basically you watch him get money while you stand around and heal him occasionally.

Haha no I am just joking about this part, I actually love playing support. But still you get my point. Every f***ing role is determined beforehand. You need to take a suppor thero, you need to go to that bottom lane, and you need to support that ADC player.

It’s not that the game forces you, but the other humans in your team – without even saying a word. Because as soon as you do something „unexpected“ they will start to flame you and WOOOW TAKE CARE report you like an 8 year old boy that starts crying because you ran over his feet with your car… well no wait that would actually hurt, so let’s go with the old „you stole my candy!“-story.

Need an example?

Leave my lane you asshole you steal my money

LOL is a team based game, so it’s all about teamplay, right?

WRONG!

Many times it happened that I was support in the bottom lane and I saw the guy in midlane struggle so I thought to myself: „Hey Marcel, you are such a good guy, you are a white knight, a hero! You must go help this poor fellow!“

So I ported to mid, helped either kill or at least scare the enemy mid guy away and protect my mid-lane team collegue.

And what happens?

Many times my mid-lane „friend“ who was supposed to be thankful that I helped him in times of danger, he actually yelled at me (in chat) saying shit like „Go back to your lane, you don’t belong here!“ „How dare you steal my money!!! Go away! Don’t last-hit the creeps!“

Is this how you thank a white knight? I doubt it.

Go back to your lane you already cost us two towers!!!

This was also a funny one. So I was in the top lane fighting against this other guy and it was suuuper boring because for 10 minutes we could not kill each other but just both try to farm as much money as possible.

Then I realized that there is a big team fight going on on the bottom lane. And in my dictionary of „How to play a f***ing moba“ if you have a team fight you better go there to help.

So again, I ported to bot, and we were able to kill some of the other guys or whatever.

Anyhow, so while we were fighting at the bottom lane, one single enemy pushed the top lane that was undefended at this time.

I immediately noticed it… because one of my teammates pinged it like 100 times on the map.

And of course he also said in the chat „What are you doing? Go back to your lane you already cost us two towers!!!!“

Now the rule # 1 in a game like LOL is of course: Never try to argue with foreign players about your playstyle, you can not win.

But sadly rule # 2 is you can NOT just be quiet. You need to say something.

So I was like „Yes I know I know the tower has gone but I was here in the bottom lane helping in the teamfight“

Of course he was not happy and threatened to report me.

Humans have optimized the fun out of League of Legends

LOL would be such a great game. The graphics are beautiful, the heroes well-designed (more or less). But the fact that you can not just pick whatever hero you want based on whatever you feel like just kills the game for me.

And you need to stay in your lane, you need to do exactly what people expect your role to do.

I don’t play games so other people can tell me how the f*** I should play my game (never tell that to others, you will lose).

This can go so far that you can play as jungler and people in your team will „command“ you to either take the blue or red buff first. Come on guys just leave me play my game ffs.

That’s why Dota 2 is so much better

Now how does this look in Dota 2?

(PS: Play „Turbo mode“ not regular „All Pick“ for more casual fun)

During character selection you can pick any character you like. Any. Nobody gives a f*** because people believe in the creation of random synergy between heroes.

During the game you can more of less just roam around on the map because people believe in your skills to react to the game and potentially gank a lane that is not „your“ lane.

People will (mostly) not flame or report you for such behaviour. Instead, everyone is happy to see and welcome you on his lane because it’s awesome that you want to help him on his lane against his enemy, especially if he is behind.

I can’t stress this enough: People are actually happy to see you on their lanes because it might give you an advantage over the enemy.

So long story short: Dota 2 > LOL 🙂