Shuffle a list of integers with Java 8 Streams API

后端 未结 8 1099
别那么骄傲
别那么骄傲 2020-11-28 09:34

I tried to translate the following line of Scala to Java 8 using the Streams API:

// Scala
util.Random.shuffle((1 to 24).toList)

To write t

相关标签:
8条回答
  • 2020-11-28 10:16

    You may find the following toShuffledList() method useful.

    private static final Collector<?, ?, ?> SHUFFLER = Collectors.collectingAndThen(
            Collectors.toCollection(ArrayList::new),
            list -> {
                Collections.shuffle(list);
                return list;
            }
    );
    
    @SuppressWarnings("unchecked")
    public static <T> Collector<T, ?, List<T>> toShuffledList() {
        return (Collector<T, ?, List<T>>) SHUFFLER;
    }
    

    This enables the following kind of one-liner:

    IntStream.rangeClosed('A', 'Z')
             .mapToObj(a -> (char) a)
             .collect(toShuffledList())
             .forEach(System.out::print);
    

    Example output:

    AVBFYXIMUDENOTHCRJKWGQZSPL
    
    0 讨论(0)
  • 2020-11-28 10:21
    public static List<Integer> getSortedInRandomOrder(List<Integer> list) {
        return list
                .stream()
                .sorted((o1, o2) -> ThreadLocalRandom.current().nextInt(-1, 2))
                .collect(Collectors.toList());
    }
    
    0 讨论(0)
  • 2020-11-28 10:28

    Here you go:

    List<Integer> integers =
        IntStream.range(1, 10)                      // <-- creates a stream of ints
            .boxed()                                // <-- converts them to Integers
            .collect(Collectors.toList());          // <-- collects the values to a list
    
    Collections.shuffle(integers);
    
    System.out.println(integers);
    

    Prints:

    [8, 1, 5, 3, 4, 2, 6, 9, 7]
    
    0 讨论(0)
  • 2020-11-28 10:29

    You can use a custom comparator that "sorts" the values by a random value:

    public final class RandomComparator<T> implements Comparator<T> {
    
        private final Map<T, Integer> map = new IdentityHashMap<>();
        private final Random random;
    
        public RandomComparator() {
            this(new Random());
        }
    
        public RandomComparator(Random random) {
            this.random = random;
        }
    
        @Override
        public int compare(T t1, T t2) {
            return Integer.compare(valueFor(t1), valueFor(t2));
        }
    
        private int valueFor(T t) {
            synchronized (map) {
                return map.computeIfAbsent(t, ignore -> random.nextInt());
            }
        }
    
    }
    

    Each object in the stream is (lazily) associated a random integer value, on which we sort. The synchronization on the map is to deal with parallel streams.

    You can then use it like that:

    IntStream.rangeClosed(0, 24).boxed()
        .sorted(new RandomComparator<>())
        .collect(Collectors.toList());
    

    The advantage of this solution is that it integrates within the stream pipeline.

    0 讨论(0)
  • This is my one line solution: I am picking one random color:

    colourRepository.findAll().stream().sorted((o1,o2)-> RandomUtils.nextInt(-1,1)).findFirst().get()
    
    0 讨论(0)
  • 2020-11-28 10:32

    If you want to process the whole Stream without too much hassle, you can simply create your own Collector using Collectors.collectingAndThen():

    public static <T> Collector<T, ?, Stream<T>> toEagerShuffledStream() {
        return Collectors.collectingAndThen(
          toList(),
          list -> {
              Collections.shuffle(list);
              return list.stream();
          });
    }
    

    But this won't perform well if you want to limit() the resulting Stream. In order to overcome this, one could create a custom Spliterator:

    package com.pivovarit.stream;
    
    import java.util.List;
    import java.util.Objects;
    import java.util.Random;
    import java.util.RandomAccess;
    import java.util.Spliterator;
    import java.util.function.Consumer;
    import java.util.function.Supplier;
    
    class ImprovedRandomSpliterator<T, LIST extends RandomAccess & List<T>> implements Spliterator<T> {
    
        private final Random random;
        private final List<T> source;
        private int size;
    
        ImprovedRandomSpliterator(LIST source, Supplier<? extends Random> random) {
            Objects.requireNonNull(source, "source can't be null");
            Objects.requireNonNull(random, "random can't be null");
    
            this.source = source;
            this.random = random.get();
            this.size = this.source.size();
        }
    
        @Override
        public boolean tryAdvance(Consumer<? super T> action) {
            if (size > 0) {
                int nextIdx = random.nextInt(size);
                int lastIdx = --size;
    
                T last = source.get(lastIdx);
                T elem = source.set(nextIdx, last);
                action.accept(elem);
                return true;
            } else {
                return false;
            }
        }
    
        @Override
        public Spliterator<T> trySplit() {
            return null;
        }
    
        @Override
        public long estimateSize() {
            return source.size();
        }
    
        @Override
        public int characteristics() {
            return SIZED;
        }
    }
    
    

    and then:

    public final class RandomCollectors {
    
        private RandomCollectors() {
        }
    
        public static <T> Collector<T, ?, Stream<T>> toImprovedLazyShuffledStream() {
            return Collectors.collectingAndThen(
              toCollection(ArrayList::new),
              list -> !list.isEmpty()
                ? StreamSupport.stream(new ImprovedRandomSpliterator<>(list, Random::new), false)
                : Stream.empty());
        }
    
        public static <T> Collector<T, ?, Stream<T>> toEagerShuffledStream() {
            return Collectors.collectingAndThen(
              toCollection(ArrayList::new),
              list -> {
                  Collections.shuffle(list);
                  return list.stream();
              });
        }
    }
    

    I explained the performance considerations here: https://4comprehension.com/implementing-a-randomized-stream-spliterator-in-java/

    0 讨论(0)
提交回复
热议问题