Java Stream HOW TO .stream() - java

I am trying and googling and can not get it right. How to write this with stream?
private final ConcurrentMap<UUID, Expression> alleMap = new ConcurrentHashMap<>(1000);
private String[] getAllDatabases()
{
Set<String> allDatabases = new HashSet<>();
for (Entry<UUID, Expression> entry : alleMap.entrySet())
{
allDatabases.add(entry.getValue().getChapter().getDatabaseName());
}
List<String> allDatabasesList = new ArrayList<>(allDatabases);
String[] result = new String[allDatabases.size()];
for (int i=0; i < result.length; i++)
{
result[i] = allDatabasesList.get(i);
}
return result;
}
alleMap.values().stream().???
The reason I need an Array is that I am writing a Swing Application and need the String[] for a JOptionPane question.

Breaking this down for you:
Set<String> allDatabases = new HashSet<>();
for (Entry<UUID, Expression> entry : alleMap.entrySet()) {
allDatabases.add(entry.getValue().getChapter().getDatabaseName());
}
This is getting all unique database names for all chapters for expressions that are values in your map.
List<String> allDatabasesList = new ArrayList<>(allDatabases);
String[] result = new String[allDatabases.size()];
for (int i=0; i < result.length; i++) {
result[i] = allDatabasesList.get(i);
}
As far as I can tell this is all just to convert the set of database names to an array. If so, you could have just used allDatabases.toArray(new String[allDatabases.size()]. Streams have a similar method (though they take an IntFunction to create an array rather than the new array):
This translates to:
alleMap.values().stream()
.map(Expression::getChapter)
.map(Chapter::getDatabaseName)
.distinct()
.toArray(String[]::new);
Note the distinct to replicate the uniqueness of sets.

Starts with not wanting arrays. They are low-level, inflexible constructs that don't play nice with generics and in general shouldn't be used unless you really know what you are doing and are willing to accept a little pain to accomodate it.
Streams are a tool. Not a universal improvement. If there's a pressing need to go with streams, by all means. But don't replace everything with streams because you think that's somehow 'better'. Just do what you're comfy with :)
If you must:
You aren't using the keys at all, just the values. So why bother with entrySet? values() will get what you want with far less effort.
List<String> alleMap.values().stream()
.map(e -> e.getChapter().getDataBaseName())
.collect(Collectors.toList());
This is pretty basic; maybe drop the specific case you're working on, and follow some tutorials first, then pick this back up, you'll get the hang of it in no time.

Related

Applying a function to list values and creating a resultant array with less code

I understand that what I have currently is maybe better for readability, but I am just interested in the different ways I could achieve the same result with less code.
ArrayList<MaterialButton> buttons = new ArrayList<>();
// grab button states and write to database
String[] buttonStates = new String[buttons.size()];
for (int i = 0; i < buttons.size(); i++) {
buttonStates[i] = String.valueOf(buttons.get(i).isChecked());
}
Appreciate any input I can get on this!
You can use the java stream api for that:
String[] buttonStates = buttons.stream()
.map(MaterialButton::isChecked) //maps to boolean (assuming isChecked returns boolean)
.map(String::valueOf) //maps to String
.toArray(String[]::new); //collects all Strings into an array
The String[]::new is a Function that takes the number of elements as an argument, meaning it creates a new String[buttons.size()].

Can the new for loop in Java be used with two variables?

We can use the old for loop (for(i = 0, j = 0; i<30; i++,j++)) with two variables
Can we use the for-each loop (or the enhanced for loop) in java (for(Item item : items) with two variables? What's the syntax for that?
Unfortunately, Java supports only a rudimentary foreach loop, called the enhanced for loop. Other languages, especially FP ones like Scala, support a construct known as list comprehension (Scala calls it for comprehension) which allows nested iterations, as well as filtering of elements along the way.
No you can't. It is syntactic sugar for using Iterator. Refer here for a good answer on this issue.
You need to have an object that contains both variables.
It can be shown on a Map object for example.
for (Map.Entry<String,String> e: map.entrySet()) {
// you can use e.getKey() and e.getValue() here
}
The following should have the same (performance) effect that you are trying to achieve:
List<Item> aItems = new List<Item>();
List<Item> bItems = new List<Item>();
...
Iterator aIterator = aItems.iterator();
Iterator bIterator = bItems.iterator();
while (aIterator.hasNext() && bIterator.hasNext()) {
Item aItem = aIterator.next();
Item bItem = bIterator.next();
}
The foreach loop assumes that there is only one collection of things. You can do something for each element per iteration. How would you want it to behave that if you could iterate over two collections at once? What if they have different lenghts?
Assuming that you have
Collection<T1> collection1;
Collection<T2> collection2;
You could write an iterable wrapper that iterates over both and returns some sort of merged result.
for(TwoThings<T1, T2> thing : new TwoCollectionWrapper(collection1, collection2) {
// one of them could be null if collections have different length
T1 t1 = thing.getFirst();
T2 t2 = thing.getSecond();
}
That's the closest what I can think of but I don't see much use for that. If both collections are meant to be iterated together, it would be simpler to create a Collection<TwoThings> in the first place.
Besides iterating in parallel you could also want to iterate sequentially. There are implementations for that, e.g. Guava's Iterables.concat()
The simple answer "No" is already given. But you could implement taking two iterators as argument, and returning Pairs of the elements coming from the two iterators. Pair being a class with two fields. You'd either have to implement that yourself, or it is probably existent in some apache commons or similar lib.
This new Iterator could then be used in the foreach loop.
I had to do one task where I need to collect various data from XML and store in SET interface and then output them to a CSV file.
I read the data and stored it in Set interface object as x,y,z.
For CSV file header, I used string buffer to hold the headers
String buffer
StringBuffer buffer = new StringBuffer("");
buffer.append("FIRST_NAME,LAST_NAME,ADDRESS\r\n")
Set<String> x = new HashSet<String>();
Set<String> y = new HashSet<String>();
Set<String> z = new HashSet<String>();
....
Iterator iterator1 = x.iterator()
Iterator iterator2 = y.iterator()
Iterator iterator3 = z.iterator()
while(iterator1.hasNext() && iterator2.hasNext() && iterator3.hasNext()){
String fN = iterator1.next()
String lN = iterator2.next()
String aDS = iterator3.next()
buffer.append(""+fN+","+lN+","+aDS+"\r\n")
}

How to simulate Pythons's dict "items()" method for Java's HashMaps?

I know I should learn how to program efficiently in Java and stop thinking it is Python; but the problem is that this kind of thing is turning out to be a small nightmare in Java.
I have the following function in Python that takes a string as an argument:
def decodeL(input):
L = []
for i in range(len(input)):
for j in x.items(): // Where "x" is the Dictionary outside the function.
if input[i].lower() in j[0]:
L.append(j[1])
return L
Ignoring the functionality of the function itself, what makes it work so well is the "items()" method, which returns me tuples inside of a list. As you can see, I play with these tuples acessing its own indexes.
In Java this is a very unpleasant problem.
static ArrayList items(HashMap hashTarget) {
ArrayList L = new ArrayList();
Set<String> keys = hashTarget.keySet();
Collection<Object> values = hashTarget.values();
for (int i = 0; i < hashTarget.size(); i++) {
L.add(keys);
L.add(values);
}
return L;
}
As you can see, I tried to implement my own "items()" method and I failed miserably; since there is no tuples natively in Java, this is really messing up with my brain :). I can't intercalate the keys and values in the same way that Python does so beautifully; therefore, I can't create the Java Version of that "decodeL" function, which is my final goal.
So, that's it; another random "challenge" for you guys.
Java has already implemented this for you in the way of the Map.entrySet() method.
for(Map.Entry<K, V> j : x.entrySet()) {
V val = j.getValue(); // j[1]
K key = j.getKey(); // j[0]
}

Java: Apply Callback to Array Values

I'm looking for a simple way to apply a callback method to each element in a String array. For instance in PHP I can make all elements in an array like this:
$array = array_map('strtolower', $array);
Is there a simple way to accomplish this in Java?
First, object arrays in Java are vastly inferior to Lists, so you should really use them instead if possible. You can create a view of a String[] as a List<String> using Arrays.asList.
Second, Java doesn't have lambda expressions or method references yet, so there's no pretty way to do this... and referencing a method by its name as a String is highly error prone and not a good idea.
That said, Guava provides some basic functional elements that will allow you to do what you want:
public static final Function<String, String> TO_LOWER =
new Function<String, String>() {
public String apply(String input) {
return input.toLowerCase();
}
};
// returns a view of the input list with each string in all lower case
public static List<String> toLower(List<String> strings) {
// transform in Guava is the functional "map" operation
return Lists.transform(strings, TO_LOWER);
}
Unlike creating a new array or List and copying the lowercase version of every String into it, this does not iterate the elements of the original List when created and requires very little memory.
With Java 8, lambda expressions and method references should finally be added to Java along with extension methods for higher-order functions like map, making this far easier (something like this):
List<String> lowerCaseStrings = strings.map(String#toLowerCase);
There's no one-liner using built-in functionality, but you can certainly match functionality by iterating over your array:
String[] arr = new String[...];
...
for(int i = 0; i < arr.length; i++){
arr[i] = arr[i].toLowerCase();
}
You could use reflection:
String[] map(java.lang.reflect.Method method, String[] array) {
String[] new_array = new String[array.length];
for (int i = 0; i < array.length; i++) new_array[i] = (String)method.invoke(null, new Object[]{array[i]});
return new_array;
}
Then you just need to declare a static method somewhere and get a reference to it using the reflection API.

How to lowercase every element of a collection efficiently?

What's the most efficient way to lower case every element of a List or Set?
My idea for a List:
final List<String> strings = new ArrayList<String>();
strings.add("HELLO");
strings.add("WORLD");
for(int i=0,l=strings.size();i<l;++i)
{
strings.add(strings.remove(0).toLowerCase());
}
Is there a better, faster way? How would this example look like for a Set? As there is currently no method for applying an operation to each element of a Set (or List) can it be done without creating an additional temporary Set?
Something like this would be nice:
Set<String> strings = new HashSet<String>();
strings.apply(
function (element)
{ this.replace(element, element.toLowerCase();) }
);
Thanks,
Yet another solution, but with Java 8 and above:
List<String> result = strings.stream()
.map(String::toLowerCase)
.collect(Collectors.toList());
This seems like a fairly clean solution for lists. It should allow for the particular List implementation being used to provide an implementation that is optimal for both the traversal of the list--in linear time--and the replacing of the string--in constant time.
public static void replace(List<String> strings)
{
ListIterator<String> iterator = strings.listIterator();
while (iterator.hasNext())
{
iterator.set(iterator.next().toLowerCase());
}
}
This is the best that I can come up with for sets. As others have said, the operation cannot be performed in-place in the set for a number of reasons. The lower-case string may need to be placed in a different location in the set than the string it is replacing. Moreover, the lower-case string may not be added to the set at all if it is identical to another lower-case string that has already been added (e.g., "HELLO" and "Hello" will both yield "hello", which will only be added to the set once).
public static void replace(Set<String> strings)
{
String[] stringsArray = strings.toArray(new String[0]);
for (int i=0; i<stringsArray.length; ++i)
{
stringsArray[i] = stringsArray[i].toLowerCase();
}
strings.clear();
strings.addAll(Arrays.asList(stringsArray));
}
You can do this with Google Collections:
Collection<String> lowerCaseStrings = Collections2.transform(strings,
new Function<String, String>() {
public String apply(String str) {
return str.toLowerCase();
}
}
);
If you are fine with changing the input list here is one more way to achieve it.
strings.replaceAll(String::toLowerCase)
Well, there is no real elegant solution due to two facts:
Strings in Java are immutable
Java gives you no real nice map(f, list) function as you have in functional languages.
Asymptotically speaking, you can't get a better run time than your current method. You will have to create a new string using toLowerCase() and you will need to iterate by yourself over the list and generate each new lower-case string, replacing it with the existing one.
Try CollectionUtils#transform in Commons Collections for an in-place solution, or Collections2#transform in Guava if you need a live view.
This is probably faster:
for(int i=0,l=strings.size();i<l;++i)
{
strings.set(i, strings.get(i).toLowerCase());
}
I don't believe it is possible to do the manipulation in place (without creating another Collection) if you change strings to be a Set. This is because you can only iterate over the Set using an iterator or a for each loop, and cannot insert new objects whilst doing so (it throws an exception)
Referring to the ListIterator method in the accepted (Matthew T. Staebler's) solution. How is using the ListIterator better than the method here?
public static Set<String> replace(List<String> strings) {
Set<String> set = new HashSet<>();
for (String s: strings)
set.add(s.toLowerCase());
return set;
}
I was looking for similar stuff, but was stuck because my ArrayList object was not declared as GENERIC and it was available as raw List type object from somewhere. I was just getting an ArrayList object "_products". So, what I did is mentioned below and it worked for me perfectly ::
List<String> dbProducts = _products;
for(int i = 0; i<dbProducts.size(); i++) {
dbProducts.add(dbProducts.get(i).toLowerCase());
}
That is, I first took my available _products and made a GENERIC list object (As I were getting only strings in same) then I applied the toLowerCase() method on list elements which was not working previously because of non-generic ArrayList object.
And the method toLowerCase() we are using here is of String class.
String java.lang.String.toLowerCase()
not of ArrayList or Object class.
Please correct if m wrong. Newbie in JAVA seeks guidance. :)
Using JAVA 8 parallel stream it becomes faster
List<String> output= new ArrayList<>();
List<String> input= new ArrayList<>();
input.add("A");
input.add("B");
input.add("C");
input.add("D");
input.stream().parallel().map((item) -> item.toLowerCase())
.collect(Collectors.toCollection(() -> output));

Categories

Resources