7

So I've created an IntStream where I give it a range of 1 - 9. I would like to be able to use the map function to take each element in the given range (1-9) and randomise each one.

Essentially I would like to stream the numbers 1 - 9 in a different order each time the program is ran. (I'm open to other ideas, but it has to be using streams).

I've heard about using Java's Random class but i'm not sure how i would implement that over a map of each element.

I've tried doing this but there are errors:

 IntStream.range(1, 9).map(x -> x = new Random()).forEach(x -> System.out.println(x));

Any help would be appreciated.

Juan Carlos Mendoza
  • 5,736
  • 7
  • 25
  • 50
SneakyShrike
  • 723
  • 1
  • 10
  • 31
  • "Essentially I would like to stream the numbers 1 - 9 in a different order each time the program is ran. (I'm open to other ideas, but it has to be using streams)" Bluntly: if this is your goal, give up. Streams are not a useful abstraction for this task, and are likely to be actively problematic. If this _isn't_ your goal, of course, then you might be able to do something. – Louis Wasserman Jan 10 '18 at 17:27
  • Can you show some sample output to clarify what you want to do? – Sweeper Jan 10 '18 at 17:30
  • 4
    `Random#ints(x, y)`? – Eugene Jan 10 '18 at 17:34
  • 2
    `x -> x = new Random()` this is not doing what you think it's doing. `x` is an integer variable, and you're attempting to assign an object of type `Random` to it. – Michael Jan 10 '18 at 17:36
  • see [this](https://howtodoinjava.com/java-8/stream-random-numbers-range/) – Hadi J Jan 10 '18 at 17:37
  • @Sweeper Okay, I would like to produce the values of 1,2,3,4,5,6,7,9 but each time it alters the order of these 9 values to be in a random order. So like, 5,7,3,2,1,4,9,8,6 for example. Ideally with streams if thats possible. – SneakyShrike Jan 10 '18 at 17:38
  • 1
    @GR412 it's not going to be. Your best bet is to have an intermediate list: `List list = IntStream.rangeClosed(1, 9).boxed().collect(toList()); Collections.shuffle(list); return list.stream();` – Louis Wasserman Jan 10 '18 at 17:40
  • @HadiJeddizahed I see, but the link you posted only seems to generate 5 random numbers. I'd like to basiclly change the order of the values 1-9 each time if that makes sense. I may have described it poorly before. – SneakyShrike Jan 10 '18 at 17:41
  • @LouisWasserman I tried using 'Collections.shuffle' before and it caused issues further on down the line. That's why i was trying to make use of the Random class. – SneakyShrike Jan 10 '18 at 17:45
  • What issues you had with `Collections.shuffle`? – Juan Carlos Mendoza Jan 10 '18 at 17:46
  • 1
    Define "issues further on down the line." (And you can pass an explicit `Random` to `Collections.shuffle`.) But seriously, `Collections.shuffle` is the correct solution to this problem. – Louis Wasserman Jan 10 '18 at 17:46
  • @LouisWasserman Yeah it does make sense, but i'm limited, because the list i'm using is a custom immutable list class. not Java's and because this custom list class isn't part of the collections libary in Java I can't really use shuffle. The idea is i would like to to stream some values, then convert that stream to one of the custom lists. There a function in there that allows you turn a stream into a custom list. – SneakyShrike Jan 10 '18 at 17:51
  • Does your custom list not implement `List` interface? If it really does not, please _do_. – Sweeper Jan 10 '18 at 17:57
  • @Sweeper I would but i'm not supposed to alter it. It's an assignment you see. – SneakyShrike Jan 10 '18 at 18:02
  • @Sweeper even if it did implement `List` it still wouldn't help as it's an immutable list. Collections.shuffle requires the list to be mutable. List.set() is officially documented as being an optional operation. Immutable lists don't support set(). – DodgyCodeException Jan 10 '18 at 18:04

3 Answers3

12

It can be done this way too using Random.ints:

new Random().ints(1,10)
        .distinct()
        .limit(9)
        .forEach(System.out::println);

Output:

9 8 4 2 6 3 5 7 1

EDIT

If you need a Stream with the values then do this:

Stream<Integer> randomInts = new Random().ints(1, 10)
        .distinct()
        .limit(9)
        .boxed();

If you need a List with the values then do this:

List<Integer> randomInts = new Random().ints(1, 10)
        .distinct()
        .limit(9)
        .boxed()
        .collect(Collectors.toList());
Juan Carlos Mendoza
  • 5,736
  • 7
  • 25
  • 50
  • Wow! this is so useful. Never knew this existed until now :) – Sweeper Jan 10 '18 at 18:08
  • Strictly speaking, there is a tiny, tiny chance that this code will run for ever. It can happen if later random numbers are never distinct from the previous ones. It's very unlikely, but not impossible. – DodgyCodeException Jan 10 '18 at 18:10
  • 3
    @DodgyCodeException: actually, we know that `Random` is not truly random but using an algorithm fixed in the specification. We can safely assume that it will always encounter all values between one and ten, eventually. – Holger Jan 10 '18 at 18:16
  • 2
    @JuanCarlosMendoza no, if `Random` were *truly* random, then the sequence 5 5 5 5 5 5 5 5 5 5 is equally as likely as 8 4 7 5 6 9 3 5 1 2 (or whatever your dice-throwing gives you). It is *possible* with truly random numbers to get an extremely long sequence of the same number (or same set of numbers). However, as Holger rightly points out, `Random` is not truly random and we know that we can rely on it generating all digits from 1 to 9. – DodgyCodeException Jan 10 '18 at 18:22
  • This is great, but is it possible to change it from an IntStream to just a Stream? Because i have a method where i convert a stream into a list and it doesn't seem to accept IntStreams as a parameter. – SneakyShrike Jan 10 '18 at 18:22
  • 1
    @GR412 just add `.boxed()` after `.limit(9)` and you will have `Stream` – Juan Carlos Mendoza Jan 10 '18 at 18:24
  • @JuanCarlosMendoza I think i'm doing something wrong. The method to convert a stream to a custom list is static and takes in (Stream stream) as the parameter and i'm adding the r as that paramter. I think i need to give it like a Stream variable. I'm not quite sure. – SneakyShrike Jan 10 '18 at 18:34
  • 2
    @GR412 what’s so important about that custom list? What’s wrong with `List list = new Random().ints(1, 10).distinct().limit(9).boxed().collect(Collectors.toList());`? – Holger Jan 10 '18 at 18:36
  • @Holger I'm required to use this custom list. Not much i can do unfortunatly. – SneakyShrike Jan 10 '18 at 18:38
  • 1
    Well yes, but is that `static` method the only way to create that list? Normally, list implementations have a “copy constructor”, accepting another list, so you could use `List list = new Random().ints(1, 10).distinct().limit(9).boxed().collect(Collectors.collectingAndThen( Collectors.toList(), CustomList::new)​);` – Holger Jan 10 '18 at 18:40
  • @JuanCarlosMendoza Your edit is perfect, thanks a lot. I used the Stream with a .boxed method. – SneakyShrike Jan 10 '18 at 18:46
6

Streams are really not suitable for this job. Like, really really.

A better way is to use Collections.shuffle:

// you can replace this with however you want to populate your array. 
// You can do a for loop that loops from 1 to 9 and add each number.
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
Collections.shuffle(list);
// now "list" is in a random order

EDIT:

Now that I know you have a custom list, here's another approach. Write two methods that converts your custom list to a normal ArrayList and the other way round as well. Convert your custom list to an ArrayList, do the shuffle, and convert it back.

Just for fun, Stream, when paralleled, can kind of produce stuff in a random order, but not really.

I ran this code for 10 times:

IntStream.range(1, 10).parallel().forEach(System.out::print);

And here were the output:

347195628
342179856
832497165
328194657
326479581
341287956
873629145
837429156
652378914
632814579
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • that generates 10 random numbers though, i'm after the values 1-9 but ordered differently each time. I should have made it clearer but it's really the ordering i want to be random each time. – SneakyShrike Jan 10 '18 at 17:53
  • What do you mean exactly? 1-9 _is_ 10 numbers. @GR412 – Sweeper Jan 10 '18 at 17:53
  • i basiclly want the values 1,2,3,4,5,6,7,8,9 but randomly ordered, say like 6,5,3,1,2,4,8,9,7. – SneakyShrike Jan 10 '18 at 17:55
  • 1
    @GR412 Yeah that's exactly what `Collection.shuffle` does. Do you not think that it does that? What do you think it does? – Sweeper Jan 10 '18 at 17:56
  • I know but the issue is i can't use that because i'm using a custom immutable class that isn't in the Java collections libary. My idea is to stream a the numbers 1-9 then convert that stream into a custom list. Theres a method in the lsit class that allows you do that you see. – SneakyShrike Jan 10 '18 at 18:00
  • @GR412 I added another approach. – Sweeper Jan 10 '18 at 18:04
  • I still think that `Collections.shuffle()` is the way to go. It's how shuffling is done. Even if you have an immutable list. In which case, copy your immutable list to an `ArrayList`, shuffle it, and copy it back to another immutable list. – DodgyCodeException Jan 10 '18 at 18:16
  • 1
    @DodgyCodeException you don’t even need an `ArrayList`, the `List` returned by `Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9)` is already sufficient. Shuffle it and construct the immutable list from it; if there’s only that factory method accepting a stream, `list.stream()` would work. – Holger Jan 10 '18 at 18:53
3
public static void main(String[] args) {
    Random random = new Random(); 
    //infinite stream of integers between 1(inclusive) and 10(exclusive)
    IntStream intStream = random.ints(1, 10);
    //from the infinite stream get a stream of 9 random and distinct integers and print them        
    intStream.distinct().limit(9).forEach(System.out::println);
}
Eritrean
  • 15,851
  • 3
  • 22
  • 28
  • This is exactly what i'm looking for but is it possible to change it from an IntStream to just a Stream? Because i have a method where i convert a stream into a list and it doesn't seem to accept IntStreams as a parameter. – SneakyShrike Jan 10 '18 at 18:22