Using the same list with threads twice in Java

I need to complete this trivial operation with threads: set a list, get the sum and sum of the first 20 elements.

That's what I meant

IntStream stream = obj.stream().mapToInt(d->d.getInt()); stream.limit(20).sum() / stream.sum(); 

However, I cannot do this because they tell me that I cannot reuse the stream, so .. I tried the following:

 List<Integer> counts = obj.stream() .mapToInt(d -> d.getInt()) .boxed().collect(Collectors.toList()); counts.stream().limit(20).sum() / counts.stream().sum(); 

However, I was told that I cannot use the amount for Stream, so I need to display ToInt again for the left and right sides of this trivial operation.

Is there a way to make this operation a more elegant and concise way using threads?

+6
source share
3 answers

Instead of collecting numbers in a List<Integer> , you can turn them into int[] with toArray() . This way, the code is a little more compact, you donโ€™t need to insert and unzip all the time, and you can turn this int[] directly into IntStream .

 int[] nums = obj.stream().mapToInt(d -> d.getInt()).toArray(); IntStream.of(nums).limit(20).sum() / IntStream.of(nums).sum(); 
+6
source

There's a cool way to do this in one parallel pass, but without using threads.

 int[] nums = obj.stream().mapToInt(Integer::intValue).toArray(); Arrays.parallelPrefix(nums, Integer::sum); int sumOfFirst20 = nums[19]; int sumOfAll = nums[nums.length-1]; 

The parallelPrefix method will calculate the sequence of partial sums of the specified function (here, plus) on the array. Thus, the first element is equal to a0; the second is a0 + a1, the third is a0 + a1 + a2, etc.

+8
source

There is no need to complicate things too much. Just double get the stream from the list:

 int totalSum = obj.stream().mapToInt(i -> i).sum(); int first20 = obj.stream().limit(20).mapToInt(i -> i).sum(); 

Yes, he will do two passes in the list (and for the second time only for the first 20 elements, so this is not a big deal), so I expect that this is what they want from you to do. It is simple, effective and readable.


To do this in one go, you can use a collector. For example, you can do this as follows:

  Map<Boolean, Integer> map = IntStream.range(0, obj.size()) .boxed() .collect(partitioningBy(i -> i < 20, mapping(i -> obj.get(i), summingInt(i -> i)))); int first20 = map.get(true); int totalSum = map.get(true) + map.get(false); 

Basically, you index indexes first. Then for each index; you divide them on the map in two lists, one for the first 20 indices and the rest in another list; as a result, we get a Map<Boolean, List<Integer>> .

Then each index is mapped to its value in the original List (with the mapping collector).

Finally, you convert each List<Integer> by summing their values โ€‹โ€‹into a single Integer . Then you just get the total amount.

Another workaround with a custom collector:

 public static Collector<Integer, Integer[], Integer[]> limitSum(int limit, List<Integer> list) { return Collector.of(() -> new Integer[]{0, 0}, (a, t) -> { a[0] += list.get(t); if(t < limit) a[1] += list.get(t); }, (a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; }, a -> a); } 

and usage example:

 List<Integer> obj = Stream.iterate(0, x -> x + 1).limit(5).collect(toList()); //[0, 1, 2, 3, 4] Integer[] result = IntStream.range(0, obj.size()) .boxed() .collect(limitSum(4, obj)); System.out.println(Arrays.toString(result)); //[10, 6] 

<h / "> As you can see, this is not very readable and, as mentioned in the comments, it may be appropriate for the loop in this case (although you were asked to use Streams).

+2
source

Source: https://habr.com/ru/post/983699/


All Articles