PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0
Showing posts with label java-stream. Show all posts
Showing posts with label java-stream. Show all posts

Saturday, November 5, 2022

[FIXED] How to check whether an object exists a List using Streams

 November 05, 2022     arraylist, java, java-stream, lambda     No comments   

Issue

I have to create a result list by adding objects.

Here is my code.

private ArrayList<Car> carsInStore ;

public boolean addCarToStore(String type, String model, String color,
                                    int maxSpeed,int year,String plateNumber) {
    for (Car car : carsInStore) {
        if (car.getPlateNumber()==plateNumber) {
            return false;
        }
        else {
            carsInStore.add(new Car(type, model, color, maxSpeed,
                                        year, plateNumber));
            return true;
        }
    }
}

I need to check if a car with the given plateNumber already exist within the ArrayList, if it exists, then a new car will not be added.

I'd like to perform the check via Java streams.

I'm using Java 11.


Solution

If you want to rewrite your method using stream, as suggested in the comments, you could check with a stream if a Car with the given plate number already exists within the List; if it does not, then you could add it to carsInStore.

Also, to check whether two String are equal you need to use the equals method. The == operator checks the content of the confronted variables, in case of references their content corresponds to the memory address of the object they point, not the actual value they represent.

private ArrayList<Car> carsInStore;

public boolean addCarToStore(String type, String model, String color, int maxSpeed, int year, String plateNumber) {
    if (carsInStore.stream()
        .filter(c -> c.getPlateNumber().equals(plateNumber))
        .findFirst()
        .isEmpty()) {
        return false;
    }

    carsInStore.add(new Car(type, model, color, maxSpeed, year, plateNumber));
    return true;
}


Answered By - Dan
Answer Checked By - Marie Seifert (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Friday, November 4, 2022

[FIXED] How to cast lambda parameter to char in the ifPresentOrElse() method

 November 04, 2022     for-loop, java, java-stream, lambda, stack     No comments   

Issue

How do I fix this code block (in ifPresentOrElse())?

I'm stuck here with:

Inconvertible types; cannot cast '<lambda parameter>' to 'char'

Please advise how to get this compiled and running.

public static boolean isBracketsInOrder1(String bracket) {
    
    Stack<Character> charStack = new Stack<>();
    static Map<Character, Character> leftToRightBracketsMap = 
                                       Map.of('{', '}', '[', ']', '(', ')');
    bracket.chars()
        .filter(i -> leftToRightBracketsMap.containsKey((char) i))
        .findFirst()
        .ifPresentOrElse((i) -> charStack.push((char) i),
            (i) -> {
                // code does not COMPILE at equals((char) i)
                return leftToRightBracketsMap.get(charStack.pop()).equals((char) i);
            });
    return true;
}

And this is the working code using for loop representing what I'm trying to implement above using streams above.

public static boolean isBracketsInOrder(String bracket) {
    Stack<Character> charStack = new Stack<>();
    static Map<Character, Character> leftToRightBracketsMap = 
                                       Map.of('{', '}', '[', ']', '(', ')');
    boolean matchFound = true;
    for (char c : bracket.toCharArray()) {
        if (leftToRightBracketsMap.containsKey(c)) charStack.push(c);
        else {
            char leftBrack = charStack.pop();
            char correctBrack = leftToRightBracketsMap.get(leftBrack);
            if (c != correctBrack) return false;
        }
    }
    return true;
}

Solution

You've introduced the code for a very basic algorithmic question - validate a string of brackets.

There are many mistakes in your code:

  • A stream doesn't act precisely like a loop, when it hits the terminal operation (which is findFirst() in your code), it's done. Code inside ifPresentOrElse() would not be executed multiple times (you probably expected the opposite). It would be invoked only once on an optional result returned by the findFirst().

  • As its second argument ifPresentOrElse() expects an instance of the Runnable interface. Method run() neither expects any arguments, no returns a value. Therefore, this attempt to define a Runnable is incorrect: (i) -> { return something; }.

  • Any lambda expressions should to conform to a particular functional interface (see). It can't appear out of nowhere.

  • Class Stack is legacy, it's still in the JDK for backward compatibility reasons. Implementations of the Deque interface should be used instead.

  • You are not checking whether the stack is empty, which can cause an EmptyStackException. If you would replace the Stack with ArrayDeque the problem will remain, method pop() will throw NoSuchElementException. You need to make sure that stack is not empty.

  • Returning true in the imperative solution is not correct. Instead, you need to return charStack.isEmpty(), because if there are some elements in the stack - sequence is not valid, there are brackets that haven't been closed.

Implementing this problem using streams requires far more efforts than a solution using a plain loop.

According to the documentation, the only place where mutation can occur in a stream is inside the collector. As a general rule, all functions used in a stream pipeline should not operate via side effects and accumulate the stated outside the stream (apart from some edge cases, see the link). Only collector's mutable container should maintain a state.

We can contract such a collector using Collecor.of() method:

public static boolean isValidBracketSequence(String brackets) {
    return brackets.chars()
        .mapToObj(c -> (char) c)
        .collect(getBracketCollector());
}

public static Collector<Character, ?, Boolean> getBracketCollector() {
    
    return Collector.of(
        BracketContainer::new,
        BracketContainer::add,
        (left, right) -> { throw new AssertionError("should not be executed in parallel"); },
        bracketContainer -> bracketContainer.isValid() && bracketContainer.isEmpty()
    );
}

That's how a mutable container might look like:

class BracketContainer {
    public static final Map<Character, Character> leftToRightBracketsMap =
        Map.of('(', ')', '[', ']', '{', '}');
    
    private Deque<Character> stack = new ArrayDeque<>();
    private boolean isValid = true;
    
    public void add(Character next) {
        if (!isValid) return;
        
        if (leftToRightBracketsMap.containsKey(next)) {
            stack.push(next);
        } else {
            compare(next);
        }
    }
    
    public void compare(Character next) {
        
        this.isValid = !isEmpty() && leftToRightBracketsMap.get(stack.pop()).equals(next);
    }
    
    public boolean isEmpty() {
        return stack.isEmpty();
    }
    
    public boolean isValid() {
        return isValid;
    }
}

main()

public static void main(String[] args) {
    System.out.println("(([])) -> " + isValidBracketSequence("(([]))")); // true
    System.out.println("(([]]) -> " + isValidBracketSequence("(([]])")); // false
    System.out.println("(([})) -> " + isValidBracketSequence("(([}))")); // false
    System.out.println("({[])) -> " + isValidBracketSequence("({[]))")); // false
    System.out.println("({[]}) -> " + isValidBracketSequence("({[]})")); // true
}

Output:

(([])) -> true
(([]]) -> false
(([})) -> false
({[])) -> false
({[]}) -> true

A link to Online Demo



Answered By - Alexander Ivanchenko
Answer Checked By - Pedro (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] Why does Stream.allMatch() return true for an empty stream?

 November 04, 2022     java, java-8, java-stream, lambda     No comments   

Issue

My colleague and I had a bug that was due to our assumption that an empty stream calling allMatch() would return false.

if (myItems.allMatch(i -> i.isValid()) { 
    //do something
}

Of course, it is kind of our fault for assuming and not reading documentation. But what I don't understand is why the default allMatch() behavior for an empty stream returns true. What was the reasoning for this? Like the anyMatch() (which contrarily returns false), this operation is used in an imperative way that departs the monad and probably used in an if statement. Considering those facts, is there any reason why having allMatch() default to true on an empty stream be desirable for majority of uses?


Solution

This is known as vacuous truth. All members of an empty collection satisfy your condition; after all, can you point to one that doesn't?

Similarly, anyMatch returns false, because you can't find an element of your collection that does match the condition. This is confusing to a lot of people, but it turns out to be the most useful and consistent way to define "any" and "all" for empty sets.



Answered By - user2357112
Answer Checked By - Terry (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to replace null value of object into list without for expression

 November 04, 2022     collections, java, java-stream, lambda, spring     No comments   

Issue

How i can do this code with stream and lambda functions, in JAVA 8:

//Replace null values with "NO"
for (Product product:
             listProduct) {
            if(product.getIsBreakStock().equals(null)) product.setIsBreakStock("NO");
}

I try with replaceAll function tutorial and foreach(), but IDE throw me an error:

listProduct.forEach(p -> 
                p.getIsBreakStock().equals(null) ? p.setIsBreakStock("NO") : p);

Required type: void Provided: Product


Solution

maybe this will help

    listProduct.stream().filter(p -> p.getIsBreakStock() == null).peek(p ->  p.setIsBreakStock("NO") ).collect(Collectors.toList());


Answered By - iroli
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How does java's double colon operator refer non-static method using class name

 November 04, 2022     java, java-stream, lambda     No comments   

Issue

I have written a code like this:

public class MyClass implements Comparable{

    int value;

    MyClass(int value){
        this.value = value;
    }
    public static void main(String[] args) {

        MyClass obj1 = new MyClass(30);
        MyClass obj2 = new MyClass(5);
        MyClass obj3 = new MyClass(15);
        List<MyClass> classList = new ArrayList<>();
        classList.add(obj1);
        classList.add(obj2);
        classList.add(obj3);

        List<MyClass> list2 =classList.stream().sorted(MyClass::compareTo).collect(Collectors.toList());
        list2.stream().forEach(x-> System.out.println(x.value));

    }

    @Override
    public int compareTo(Object o) {
        MyClass obj = (MyClass)o;
        if(this.value<obj.value){
            return +1;
        }else return -1;
    }
}

This code compiles successfully gives me the correct output as expected-

30
15
5

I have 2 questions

  1. How are we able to refer non-static method compareTo using Myclass(classname)? Shouldn't we be expected to use object of my class to refer compareTo()?

  2. How are we able to use compareTo() method which takes only 1 parameter, whereas sorted() expects Comparator Interface whose abstract class compare() takes 2 parameters?


Solution

The Stream method sorted takes a Comparator<T> as parameter. This is a functional interface, so you can use a lambda expression or method reference as parameter that adheres to this interface. The method signature is as follows:

int compare(T var1, T var2);

So, for example, the following lambda expression is valid:

(MyClass a, MyClass b) -> a.compareTo(b)

A method reference can refer to static methods, but in this specific context, the type of the method reference can also replace the first parameter, so the following expression is equivalent to the above:

MyClass::compareTo

Similarly, you can do the following:

(String a, String b) -> a.equals(b)
String::equals

Or things like this:

(String a) -> "b".equals(a)
"b"::equals


Answered By - Jeroen Steenbeeke
Answer Checked By - Candace Johnson (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to split odd and even numbers and sum of both in a collection using Stream

 November 04, 2022     collections, java, java-8, java-stream, lambda     No comments   

Issue

How can I split odd and even numbers and sum both in a collection using stream methods of Java 8?

public class SplitAndSumOddEven {

    public static void main(String[] args) {

        // Read the input
        try (Scanner scanner = new Scanner(System.in)) {

            // Read the number of inputs needs to read.
            int length = scanner.nextInt();

            // Fillup the list of inputs
            List<Integer> inputList = new ArrayList<>();
            for (int i = 0; i < length; i++) {
                inputList.add(scanner.nextInt());
            }

            // TODO:: operate on inputs and produce output as output map
            Map<Boolean, Integer> oddAndEvenSums = inputList.stream(); // Here I want to split odd & even from that array and sum of both

            // Do not modify below code. Print output from list
            System.out.println(oddAndEvenSums);
        }
    }
}

Solution

You can use Collectors.partitioningBy which does exactly what you want:

Map<Boolean, Integer> result = inputList.stream().collect(
       Collectors.partitioningBy(x -> x%2 == 0, Collectors.summingInt(Integer::intValue)));

The resulting map contains sum of even numbers in true key and sum of odd numbers in false key.



Answered By - Tagir Valeev
Answer Checked By - David Marino (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How do I compare values of an Object with the StreamAPI and Lambda expressions?

 November 04, 2022     collections, java, java-stream, lambda     No comments   

Issue

I got a problem with streams. I'm trying to compare everything in my LinkedList to get the latest Medium with the StreamAPI. The problem I run into is that my methode sucheNeuesMedium() throws NoSuchElementExeption, because get() finds no value. The stream is filled with elements (befor anyone says "be sure that you got objects in your list"). I want to compare them by the value of "Jahr" (it has to be with the StreamAPI and Lambda expressions). Any ideas how I can make this run? If you need further information I will supply you.

    //In class Medienverwaltung
    LinkedList<Medium> liste = new LinkedList<Medium>();
    Iterator<Medium> it = liste.iterator(); //irrelevant for this task
    Stream<Medium> stream = liste.stream();

    public Medium sucheNeuesMedium() {
        return stream.max(Comparator.comparing(Medium::getJahr)).get();
    }

    //In abstract class Medium
    public int getJahr() {
        return jahr;
    }

Solution

I found my error: I have to operate with liste.stream() and then my comparison. I always tried it on Imported objects (serialized), meaning the stream stayed empty (the stream got initialized with the empty list). But if I use liste.stream().max(Comparator.comparing(Medium::getJahr)).get(); it works flawless and multiple times not once. I didn't think about the stream being used aswell. It works, but still thanks for the fast answers.



Answered By - Hristian
Answer Checked By - Senaida (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How could I make a stream pipeline more variable in Java?

 November 04, 2022     functional-programming, java, java-stream, lambda     No comments   

Issue

I wrote a stream pipeline:

private void calcMin(Clazz clazz) {
    OptionalInt min = listOfObjects.stream().filter(y -> (y.getName()
                    .matches(clazz.getFilter())))
                    .map(y -> (y.getUserNumber()))
                    .mapToInt(Integer::intValue)
                    .min();
    list.add(min.getAsInt());
}

This pipeline gives me the lowest UserNumber.

So far, so good.

But I also need the greatest UserNumber. And I also need the lowest GroupNumber. And also the greatest GroupNumber.

I could write:

private void calcMax(Clazz clazz) {
    OptionalInt max = listOfObjects.stream().filter(y -> (y.getName()
                    .matches(clazz.getFilter())))
                    .map(y -> (y.getUserNumber()))
                    .mapToInt(Integer::intValue)
                    .max();
    list.add(max.getAsInt());
}

And I could also write the same for .map(y -> (y.getGroupNumber())).

This will work, but it is very redudant.

Is there a way to do it more variable?


Solution

There are two differences in the examples: the map() operation, and the terminal operation (min() and max()). So, to reuse the rest of the pipeline, you'll want to parameterize these.

I will warn you up front, however, that if you call this parameterized method directly from many places, your code will be harder to read. Comprehension of the caller's code will be easier if you keep a helper function—with a meaningful name—that delegates to the generic method. Obviously, there is a balance here. If you wanted to add additional functional parameters, the number of helper methods would grow rapidly and become cumbersome. And if you only call each helper from one place, maybe using the underlying function directly won't add too much clutter.

You don't show the type of elements in the stream. I'm using the name MyClass in this example as a placeholder.

    private static OptionalInt extremum(
        Collection<? extends MyClass> input,
        Clazz clazz,
        ToIntFunction<? super MyClass> valExtractor,
        Function<IntStream, OptionalInt> terminalOp) {

        IntStream matches = input.stream()
            .filter(y -> y.getName().matches(clazz.getFilter()))
            .mapToInt(valExtractor);
        return terminalOp.apply(matches);
    }

    private OptionalInt calcMinUserNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getUserNumber, IntStream::min);
    }

    private OptionalInt calcMaxUserNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getUserNumber, IntStream::max);
    }

    private OptionalInt calcMinGroupNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getGroupNumber, IntStream::min);
    }

    private OptionalInt calcMaxGroupNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getGroupNumber, IntStream::max);
    }

    ...

And here's a usage example:

calcMaxGroupNumber(clazz).ifPresent(list::add);


Answered By - erickson
Answer Checked By - Senaida (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to give discounts based on category and quantity in java 8?

 November 04, 2022     java, java-stream, lambda     No comments   

Issue

I have a data set like this.

name: tea, pricePerUnit: 5.3, quantity: 2, category: drinks
name: coffee, pricePerUnit: 3.5, quantity: 2, category: drinks
name: cheese, pricePerUnit: 8, quantity: 2, category: food

Based on category and the priceUnit I have to give some discount like

If the client buys more than 3 items of the product within the same category, we are giving him 10% discount for all product in this category.

So, I have implemented this method but It has a bug.

public BigDecimal calculateTotalWithDiscountAndCategory(ShoppingCart shoppingCart){

        return  shoppingCart.getProducts()
                .stream()
                .collect(Collectors.groupingBy(Product::getCategory))
                .entrySet()
                .stream()
                .map( v -> v.getValue()
                        .stream()
                        .map(product ->{
                            if(product.getQuantity() > 3){
                                BigDecimal currentTotal = product.getPricePerUnit().multiply(BigDecimal.valueOf(product.getQuantity()));
                                return currentTotal.multiply(BigDecimal.valueOf(0.9));

                            }else {
                                return product.getPricePerUnit().multiply(BigDecimal.valueOf(product.getQuantity()));
                            }
                                }
                        )
                        .reduce(BigDecimal::add)
                        .orElse(BigDecimal.ZERO)

                ).reduce(BigDecimal::add)
                .orElse(BigDecimal.ZERO);

    }

If I executed this, Every time no discount calculating because it is taking care of one object which quantity < 2.

But correct one would be, As there are 4 drinks discounts should apply to both drinks.

How can I modify this code?


Solution

You need to aggregate the quantity before applying the discount. Here's one way to do that:

public BigDecimal calculateTotalWithDiscountAndCategory(ShoppingCart shoppingCart){
    Map<Category, List<Product>> products = shoppingCart.getProducts()
            .stream()
            .collect(Collectors.groupingBy(Product::getCategory));
    
    return products.values()
            .stream()
            .map(this::categoryTotal)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
}

private BigDecimal categoryTotal(List<Product> products) {
    BigDecimal sum = products.stream()
            .map(Product::getPricePerUnit)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

    int quantity = products.stream()
            .mapToInt(Product::getQuantity)
            .sum();

    if (quantity > 3) {
        sum = sum.multiply(new BigDecimal("0.9"));
    }

    return sum;
}

A more efficient implementation might aggregate the quantity and price in the initial pass so you don't have to iterate each category twice, but for that you'll need a mutable container or a clunky merge function. I'll leave that as an exercise to the reader.



Answered By - shmosel
Answer Checked By - Senaida (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

[FIXED] How to use three streams in java 8 lambdas together?

 November 04, 2022     java, java-stream, lambda     No comments   

Issue

I have a class Country that contains a List<City>, a class City contains a List<Street>, and a class Street contains a class Humanwith a unique ID. I can't understand where is my mistake. I know there are brackets missing somewhere, but I don't know where to put them.

Human finded = country.getCities()
                .stream()
                .map(cty-> cty.getStreets()
                        .stream()
                        .map(strt-> strt.getHumans()
                                .stream()
                                .filter(hmn-> hmn.getID().equals(wantedID))
                                        .findFirst()
                                        .orElse(null);

Solution

map turns a single element in a stream into a single other element.

It's useless here - you want to turn, say, a single element (a City instance) into any number of Street elements. Could be 0 (bit of a weird City), could be 20,000. Mapping a single City to a single Street isn't going to work.

Thus, you want flatMap instead, which turns a single element in a stream into any number (0, 1, or a million - whatever you like) of elements. Given that one element needs to be turned into X elements, you can't just "return an element" the way map works. Instead, you return a stream.

county.getCities().stream()
  .flatMap(c -> c.getStreets().stream())
  .forEach(System.out::println)
  ;

would print every street in all cities, one per line.



Answered By - rzwitserloot
Answer Checked By - Robin (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Thursday, November 3, 2022

[FIXED] How to use a Java8 lambda to sort a stream in reverse order?

 November 03, 2022     java, java-8, java-stream, lambda     No comments   

Issue

I'm using java lambda to sort a list.

how can I sort it in a reverse way?

I saw this post, but I want to use java 8 lambda.

Here is my code (I used * -1) as a hack

Arrays.asList(files).stream()
    .filter(file -> isNameLikeBaseLine(file, baseLineFile.getName()))
    .sorted(new Comparator<File>() {
        public int compare(File o1, File o2) {
            int answer;
            if (o1.lastModified() == o2.lastModified()) {
                answer = 0;
            } else if (o1.lastModified() > o2.lastModified()) {
                answer = 1;
            } else {
                answer = -1;
            }
            return -1 * answer;
        }
    })
    .skip(numOfNewestToLeave)
    .forEach(item -> item.delete());

Solution

You can adapt the solution you linked in How to sort ArrayList<Long> in Java in decreasing order? by wrapping it in a lambda:

.sorted((f1, f2) -> Long.compare(f2.lastModified(), f1.lastModified())

note that f2 is the first argument of Long.compare, not the second, so the result will be reversed.



Answered By - Adrian Leonhard
Answer Checked By - Candace Johnson (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Tuesday, October 25, 2022

[FIXED] How to create different objects from a List of Strings?

 October 25, 2022     java, java-stream, oop     No comments   

Issue

I have a file with a various figures like this:

SQUARE;10

SQUARE;20

RECTANGLE;10;30

CIRCLE;10

After adding the lines to the String List, I would like it to create all the objects after one run through the list. how to do it when, for example, a rectangle has two parameters and the rest only one?

So far this is my code which works but it is buggy, because for each type of figure I have to go through the list again, I also can't use switch for this task.

    BufferedReader br = new BufferedReader(new FileReader("figury.txt"));
    List<List<String>> list1 = new ArrayList<>();
    String s;
    while ((s = br.readLine()) != null) {
        List<String> list = Arrays.asList(s.split(";"));
        list1.add(list);

    }
    list1.stream().filter(k -> k.contains("SQUARE")).forEach(i -> new Square(Integer.parseInt(i.get(1))));
    list1.stream().filter(k -> k.contains("CIRCLE")).forEach(i -> new Circle(Integer.parseInt(i.get(1))));
    list1.stream().filter(k -> k.contains("RECTANGLE")).forEach(i -> new Rectangle(Integer.parseInt(i.get(1)), Integer.parseInt(i.get(2))));

All figures are subClasses of abstract class Figure


Solution

Try this

        Map<String, Function<List<String>, Figure>> converters = new HashMap<>();
        converters.put("SQUARE", l -> new Square(Integer.parseInt(l.get(1))));
        // Repeat for each shape

        List<Figure> list = list1.stream().map(l -> converters.get(l.get(0)).apply(l))
                .collect(Collectors.toList());


Answered By - talex
Answer Checked By - Clifford M. (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Monday, September 19, 2022

[FIXED] Why is my `Stream` being closed at runtime?

 September 19, 2022     collectors, consumer, java, java-stream     No comments   

Issue

I have this bit of code:

var blong = Stream.iterate(BigInteger.ZERO, bi -> bi.add(BigInteger.ONE))
    .collect(Collector.of(
        () -> Stream.of(),
        (s, bi) -> Stream.concat(s, Stream.of(bi)),
        (s1, s2) -> Stream.concat(s1, s2),
        s -> s
    ));

System.out.println(blong.getClass().getName());

It doesn't work properly. I'm getting an IllegalStateException:

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    at java.base/java.util.stream.AbstractPipeline.spliterator(AbstractPipeline.java:346)
at java.base/java.util.stream.Stream.concat(Stream.java:1618)
at UninitializedTest.lambda$2(UninitializedTest.java:28)
at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.base/java.util.stream.Stream$1.tryAdvance(Stream.java:1469)
at java.base/java.util.Spliterator.forEachRemaining(Spliterator.java:332)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
at UninitializedTest.main(UninitializedTest.java:27)

It appears that the Streams being returned by my Supplier are being closed immediately upon creation.

Even if I create a Stream from an empty List or a Stream with some actual BigInteger data in it, I get the same error.

Why is my stream being closed?


Solution

To begin with, let's dissect the custom collector you've created:

Collector.of(
    () -> Stream.of(),                          // Container - this object is meant accumulate the result by consuming stream elements (in this case it can't really change its state, but nominally its still a container)
    (s, bi) -> Stream.concat(s, Stream.of(bi)), // Accumulator - is meant to define how stream elements should be accumulated in the container (again in this case we can't do anything with the container)
    (s1, s2) -> Stream.concat(s1, s2),          // Combiner - difines how containers should be merged while executing stream in parallel (NB: - we can replace it with a method reference Stream::concat)
    s -> s                                      // Finisher function - discribes the final transformation which should be performed with contaner (NB: since it doesn't perform any action we can omit this agument, there's an overloaded version which doesn't expect Finisher)
)

First of all it's worth to point out that Stream is not a container of data (like a Collection).

Hence, providing an empty stream () -> Stream.of() as a container of the collector is a mistake - the container needs to be mutable. But we can't push elements into an empty stream.

Secondly, the accumulator (the second argument of Collector.of()) of your custom collector is not doing what you probably expect it to do.

Accumulator is a BiConsumer<R, T> and you've implemented it as follows:

(s, bi) -> Stream.concat(s, Stream.of(bi))

Here Stream.concat() consumes all the elements from the stream s and a stream returned by Stream.of(bi) and generates a new unnamed stream, which very soon becomes a prey of the Garbage Collector. Reminder: BiConsumer doesn't return a value, so the stream returned by concat() vanishes.

The stream s remains (meaning the collector knows it's reference), but it's already consumed while executing concat(), i.e. it's closed. It happens at the point when the first stream element (BigInteger.ZERO) gets processes. And when the collector tries to process the second element, you're getting an exception because concat() attempts to consume the stream s which has been already closed.


When the Consumer fires, I'm expecting the Stream<Stream<BigInteger>> to be consumed, returning a Stream<BigInteger>

Firstly, BiConsumer as well as Consumer has an abstract method accept() which is void, it isn't meant to return anything.

It appears that the Streams being returned by my Supplier are being closed

Secondly, it feels like you have a misconception of how Collector work. An instance of a mutable container would be created only once in the sequential scenario of execution (and one container per thread in parallel, unless you specify that it's a concurrent collector by providing Collector.Characteristics.CONCURRENT, in such case all thread would share the same container).

Container should be a mutable object (otherwise it would not be useful like in your case), and its job is to accumulate stream elements. I.e. container changes its state, while collector consumes elements from the stream.

The stream returned by Stream.iterate(seed,f) would be of type Stream<BigInteger>.

The Container produced by supplier () -> Stream.of() would be of type Stream<Object> because the compiler can't infer the type of empty stream, unless you explicitly specify it using Type-Witness like .<BigInteger>of().

For that reason, the stream returned by concat() in side the accumulator would be also of type Stream<Object>. And reminder this stream would be ignored.

There wouldn't be such a beast like Stream<Stream<BigInteger>> anywhere, either in the pipeline, or inside the collector.

And lastly, I'll reiterate that it's inherently impossible to add an element into a Stream.



Answered By - Alexander Ivanchenko
Answer Checked By - Katrina (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Monday, June 27, 2022

[FIXED] How to find the largest Count of Objects assosiated with the same Object

 June 27, 2022     algorithm, arraylist, graph, java, java-stream     No comments   

Issue

I have a Mentor class in which I have an ID for each mentor and an ArrayList of mentee IDs, like this:

public class Mentor {
    
    int mentorId;
    ArrayList<Integer> mentees = new ArrayList<>();

    public Mentor(int mentorId, ArrayList<Integer> mentees) {
        this.mentorId = mentorId;
        this.mentees = mentees ;
    }
}

The problem is that some mentees can be mentors too.

I would like to somehow get a count of all of the mentees associated to the top mentor as well as how many mentors are under the top mentor.

So, basically, if a mentor has a mentee, who is also a mentor, then this mentor's mentees are also associated to the top mentor.

So, my thinking was to loop through the mentee-list and see if any of the id's match an ID of Mentor. If true, Add this mentor's list of mentees to a list and loop again, but this will not work dynamically.

My main class looks something like this:

    ArrayList<Mentor> mentors = new ArrayList<>();
    ArrayList<Integer> mentees = new ArrayList<>();
    ArrayList<Integer> mentees2 = new ArrayList<>();

    mentees.add(2);
    mentees.add(3);
    mentees2.add(4);
    mentees2.add(5);

    //[1,{2,3}]
    mentors.add(new Mentor(1, mentees));
    //[2,{4,5}]
    mentors.add(new Mentor(2, mentees2));
    int mentorCount = 0;
    int menteeCount = 0;
    for (Mentor mentor : mentors) {

        for (Integer mentee : mentees) {
            mentorCount++;
            if (mentee == mentor.mentorId){
                mentorCount++;
                //add each mentee to arraylist and start the process again, but is there an easier way to do this.
            }
        }
    }

I was wondering if there is a way of solving this, maybe using streams?


Solution

Firstly, let's briefly recap the task.

You have a set of mentors, each mentor has a collection of mentees. Some of them might happen also to be mentors and so on.

Class design

From the perspective of class design, the solution is pretty simple: you need only one class to describe this relationship - Mentor. And each Mentor should hold a reference to a collection of Mentors (not integer ids).

In your domain model, as you described it, there's no substantial difference between a mentor and a mentee. Mentor points to other mentors - is a simplest way to model that. Mentee is just an edge case, a mentor which has an empty collection of mentors.

You don't need to include classes and features in your application that doesn't bring benefit.

Data structure

From the perspective of data structures, this problem can be described very well with an acyclic disjointed Graph.

Acyclic because if we consider the relationship between mentors when a mentor could indirectly point at themself (i.e. a mentor N has a mentee, with in tern points to another mentee that happens to be also a mentor of mentor N) the task becomes ambiguous. Therefore, I'm making an assumption that no one can be a mentor to himself.

I also depicted this data structure as disjointed because mentors in this model (as well as in real life) can form isolated groups, which in graph theory called connected components. That means that there could be several mentors with the same count of mentee, which happens to be the largest.

Depth first search

In order to find all the vertices (mentors) connected with a particular mentor, we have a classic traversal algorithm, which is called Depth first search (DFS). The core idea of DFS is to peek a single neighbor of the given vertex, then in turn peek one of its neighbors and so on until the hit the vertex that doesn't point to the other vertex (i.e. maximum depth is reached). And then it should be done with every other neighbors of the previously visited vertices.

There are two ways to implement DFS.

Iterative approach

For that, we need a stack. It will store all unvisited neighbors of the previously encountered vertexes. The last vertex from each list of neighbors in every branch will be explored first because it will be placed on the top of the stack. Then it will get removed from the stack and it's neighbors will be placed on the stack. This process repeats in a loop until the stack contains at least one element.

The most performant choice of collection for the stack is ArrayDeque.

Because this approach require continuous modification of the stack by adding and removing vertices, it isn't suitable to be implemented with Stream IPA.

Recursive approach

The overall principle is the same as described above, but we don't need to provide a stack explosively. The call stack of the JVM will be utilized for that purpose.

With this approach, there's also some room to apply streams. For that reason, I've chosen the recursive implementation. Also, its code is probably a bit easier to understand. But keep in mind that recursion has certain limitations, especially in Java, and not suitable for processing a large set of data.

Recursion

A quick recap on recursion.

Every recursive implementation consists of two parts:

  • Base case - that represents a simple edge-case for which the outcome is known in advance. For this task, the base case is the given vertex has no neighbors. That means menteesCount of this vertex needs to be set to 0 because it has no mentee. And the return value for the base case is 1 because this vertex, in turn, is a valid mentee of another vertex that holds a reference to it.
  • Recursive case - a part of a solution where recursive calls a made and where the main logic resides.

The recursive case could be implemented using streams and entails recursive invocation of the for every neighbor of the given vertex. Each of these values will contribute to the menteesCount of the given vertex.

The value returned by the method will be menteesCount + 1 because for the vertex which triggered this method call, the given vertex will be a mentee as well as its mentees.

Implementation

Class mentor basically serves as a vertex of the graph. Each vertex has a unique id and collection of adjacent vertexes.

Also, in order to reuse values obtained by performing DFS I've added a field menteesCount which is initially initialized to -1 in order to distinguish between vertices that has no adjacent vertices (i.e. menteesCount has to be 0) and vertices which value wasn't calculated. Every value will be established only ones and then reused (another approach will be to utilize a map for that purpose).

Method getTopMentors() iterates over the collection of vertices and invokes DFS for every vertex which value wasn't calculated yet. This method returns a list of mentors with the highest number of associated mentees

Method addMentor() that takes a vertex id, and id of its neighbors (if any) was added in order to interact with the graph in a convenient way.

Map mentorById contains every vertex that was added in the graph and, as its name suggests, allows retrieving it based on the vertex id.

public class MentorGraph {
    private Map<Integer, Mentor> mentorById = new HashMap<>();
    
    public void addMentor(int mentorId, int... menteeIds) {
        Mentor mentor = mentorById.computeIfAbsent(mentorId, Mentor::new);
        for (int menteeId: menteeIds) {
            mentor.addMentee(mentorById.computeIfAbsent(menteeId, Mentor::new));
        }
    }
    
    public List<Mentor> getTopMentors() {
        List<Mentor> topMentors = new ArrayList<>();
        for (Mentor mentor: mentorById.values()) {
            if (mentor.getCount() != -1) continue;
            performDFS(mentor);
    
            if (topMentors.isEmpty() || mentor.getCount() == topMentors.get(0).getCount()) {
                topMentors.add(mentor);
        
            } else if (mentor.getCount() > topMentors.get(0).getCount()) {
    
                topMentors.clear();
                topMentors.add(mentor);
            }
        }
        return topMentors;
    }
    
    private int performDFS(Mentor mentor) {
        if (mentor.getCount() == -1 && mentor.getMentees().isEmpty()) { // base case
            mentor.setCount(0);
            return 1;
        }
        
        int menteeCount = // recursive case
            mentor.getMentees().stream()
                .mapToInt(m -> m.getCount() == -1 ? performDFS(m) : m.getCount() + 1)
                .sum();
        
        mentor.setCount(menteeCount);
        return menteeCount + 1;
    }
    
    public static class Mentor {
        private int id;
        private Set<Mentor> mentees = new HashSet<>();
        private int menteesCount = -1;
        
        public Mentor(int id) {
            this.id = id;
        }
        
        public boolean addMentee(Mentor mentee) {
            return mentees.add(mentee);
        }
    
        // getters, setter for menteesCount, equeals/hashCode
    }
}

An example of the graph used as a demo.

enter image description here

main() - the code models the graph shown above

public static void main(String[] args) {
    MentorGraph graph = new MentorGraph();

    graph.addMentor(1, 3, 4);
    graph.addMentor(2, 5, 6, 7);
    graph.addMentor(3, 8, 9);
    graph.addMentor(4, 10);
    graph.addMentor(5, 11, 12);
    graph.addMentor(6);
    graph.addMentor(7, 13, 14);
    graph.addMentor(8);
    graph.addMentor(9, 16, 17, 18);
    graph.addMentor(10);
    graph.addMentor(11, 18);
    graph.addMentor(12);
    graph.addMentor(13);
    graph.addMentor(14, 19, 20);
    graph.addMentor(15);
    graph.addMentor(16, 21, 22);
    graph.addMentor(17);
    graph.addMentor(18);
    graph.addMentor(19);
    graph.addMentor(20);
    graph.addMentor(21);
    graph.addMentor(22);
    
    graph.getTopMentors()
         .forEach(m -> System.out.printf("mentorId: %d\tmentees: %d\n", m.getId(), m.getCount()));
}

Output

mentorId: 1 mentees: 10
mentorId: 2 mentees: 10


Answered By - Alexander Ivanchenko
Answer Checked By - Timothy Miller (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Older Posts Home
View mobile version

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
All Comments
Atom
All Comments

Copyright © PHPFixing