繁体   English   中英

随机迭代ArrayList <Integer> 在Java中

[英]Randomly iterate over ArrayList<Integer> in Java

似乎是一个非常基本的问题。 我有一个ArrayList<Integer> al ,我想迭代它。 一般,

for(int i : al) {
    // some code
}

诀窍。 但我的要求是不是按顺序迭代而是随机迭代。

您可以在列表中使用Collections.shuffle()

请注意,这将使列表本身洗牌,因此如果订单很重要,您应该复制它(并随机播放副本)。

List<Customer> newList = new ArrayList<>( oldList ) ;
Collections.shuffle( newList ) ;

或者,您可以创建一个随机数组,其中包含元素0 - List.size()-1并使用它们作为索引来访问List中的“random”元素。

使用以下类:

import java.util.Enumeration;
import java.util.Random;

public class RandomPermuteIterator implements Enumeration<Long> {
    int c = 1013904223, a = 1664525;
    long seed, N, m, next;
    boolean hasNext = true;

    public RandomPermuteIterator(long N) throws Exception {
        if (N <= 0 || N > Math.pow(2, 62)) throw new Exception("Unsupported size: " + N);
        this.N = N;
        m = (long) Math.pow(2, Math.ceil(Math.log(N) / Math.log(2)));
        next = seed = new Random().nextInt((int) Math.min(N, Integer.MAX_VALUE));
    }

    public static void main(String[] args) throws Exception {
        RandomPermuteIterator r = new RandomPermuteIterator(100);
        while (r.hasMoreElements()) System.out.print(r.nextElement() + " ");
        //output:50 52 3 6 45 40 26 49 92 11 80 2 4 19 86 61 65 44 27 62 5 32 82 9 84 35 38 77 72 7 ...
    }

    @Override
    public boolean hasMoreElements() {
        return hasNext;
    }

    @Override
    public Long nextElement() {
        next = (a * next + c) % m;
        while (next >= N) next = (a * next + c) % m;
        if (next == seed) hasNext = false;
        return  next;
    }
}

这种方式怎么样(功能更强); 它甚至包括一个main()来演示。 基本上,在列表大小范围内生成随机数,直到您拥有其中一个。 我们使用HashSet来处理我们范围内的重复随机数。 然后,将迭代委托给索引hashset的迭代器。 “hasNext()”的逻辑通过委托变得微不足道。

public class RandomIterator<T> implements Iterator<T> {

    private Iterator<Integer> indicies;

    List<T> delegate;

    public static void main(String[] args) {
        Random r = new Random();

        List<Integer> numbers = IntStream.generate(r::nextInt).limit(10).boxed().collect(toCollection(ArrayList::new));
        List<Integer> results = new ArrayList<>();

        for(RandomIterator<Integer> test = new RandomIterator<>(numbers); test.hasNext(); ) {
            results.add(test.next());
        }
        System.out.println("In list: " + numbers);
        System.out.println("random iteration " + results);
        if(results.containsAll(numbers) && results.size() == numbers.size())
            System.out.println("Everything accounted for");
        else
            System.out.println("Something broke!");

    }

    public RandomIterator(List<T> delegate) {
        Random r = new Random();
        this.delegate = delegate;
        Set<Integer> indexSet = new LinkedHashSet<>();
        while(indexSet.size() != delegate.size())
            indexSet.add(r.nextInt(delegate.size()));

        System.out.println(indexSet);
        indicies = indexSet.iterator();
    }

    @Override
    public boolean hasNext() {
        return indicies.hasNext();
    }

    @Override
    public T next() {
        return delegate.get(indicies.next());
    }
}

如果您只想要打印100个随机数:

     IntStream.generate(r::nextInt).limit(100).forEach(System.out::println);

如果你想要一个迭代器(无论出于何种原因:)

IntStream.generate(r::nextInt).limit(100).boxed().collect(Collectors.toList()).iterator();
public class RandomIterator<T> implements Iterator<T> {

  private static final SecureRandom RANDOM = new SecureRandom();

  private final int[] order;

  private final List<T> elements;

  public RandomIterator(List<T> elements) {
     this.elements = elements;
     this.order = generateRandomOrder(elements.size());
  }

  private int[] generateRandomOrder(int size) {
     int[] index = new int[size];
     for (int i = 0; i < size; i++) {
        index[i] = i;
     }

     int swap;
     for (int i = 0; i < size; i++) {
        int randomIndex = getRandomInt(0, size);
        swap = index[i];
        index[i] = index[randomIndex];
        index[randomIndex] = swap;
     }

     return index;
  }

  private int currentIndex = 0;

  private int getRandomInt(int lowerBound, int upperBound) {
     return RANDOM.nextInt(upperBound - lowerBound) + lowerBound;
  }

  @Override
  public boolean hasNext() {
     return currentIndex != elements.size();
  }

  @Override
  public T next() {
     return elements.get(order[currentIndex++]);
  }
}

这仅在您保证列表中的元素位于索引[0,大小]之间的位置时才有效。

如果您担心SecureRandom的性能损失,您可以使用正常的随机数(我更喜欢安全,比普通随机慢8倍但安全!)。

这是一个改组迭代器,在请求少量元素时非常节省空间和时间。 在所有其他情况下,这也是非常合理的; 运行时和空间都是O(n)。 唯一的动态结构是HashMap,其大小只有输入列表的一半(虽然它在开头和结尾附近会小很多)和整数键。

static <A> Iterator<A> shuffledIterator(final List<A> l) {
  return new Iterator<A>() {
    Random randomizer = new Random();
    int i = 0, n = l.size();
    HashMap<Integer, A> shuffled = new HashMap();

    public boolean hasNext() { return i < n; }

    public A next() {
      int j = i+randomizer.nextInt(n-i);
      A a = get(i), b = get(j);
      shuffled.put(j, a);
      shuffled.remove(i);
      ++i;
      return b;
    }

    A get(int i) {
      return shuffled.containsKey(i) ? shuffled.get(i) : l.get(i);
    }
  };
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM