為了方便,我就以list為例:
算法思路
- 第一種方法的思路是:用set存儲已經取過的下標,每次循環取的時候,判斷是否已經取過
如果已經取過就跳過本次循環,一直到取num為止
這個方法不推薦使用,可能會發生死循環
如果每一次的數據值都是同一個,那么就是死循環了,雖然概論極其低,但是不代表沒有
public static List getListUnique1(List sources, int num) {
if (sources.size() <= num) {
return sources;
}
// 復制一份,以免對原數據造成影響
List _sources = new ArrayList(sources);
List targetList = new ArrayList(num);
Set<Integer> gotIndexs = new HashSet<>(num); // 以獲取的下標
Random random = new Random();
while (gotIndexs.size() < num) {
int i = random.nextInt(_sources.size());
// 已經獲取過了,跳過本次循環
if (gotIndexs.contains(i)) continue;
// 如果沒有獲取過,則放入數據
targetList.add(_sources.get(i));
gotIndexs.add(i);
}
return targetList;
}
- 第二種方法思路是:每一次循環取完數據后,從目標list中移除本次取的數據,list的大小也變小了,下一次循環產生的隨機數最大值也是list的size,所以不會發生越界問題。
public static List getListUnique2(List sources, int num) {
if (sources.size() <= num) {
return sources;
}
// 復制一份,以免對原數據造成影響
List _sources = new ArrayList(sources);
List targetList = new ArrayList(num);
Random random = new Random();
for (int k = 0; k < num; k++) {
int i = random.nextInt(_sources.size());
targetList.add(_sources.get(i));
// 取完后,從目標list內移除該數據
_sources.remove(i);
}
return targetList;
}
- 第三中方法思路:每一次循環取完數據后,把list size - k -1 的元素 放到本次取到的index位置,下次循環的隨機數最大值為list size - k
public static List getListUnique3(List sources, int num) {
if (sources.size() <= num) {
return sources;
}
// 復制一份,以免對原數據造成影響
List _sources = new ArrayList(sources);
List targetList = new ArrayList(num);
Random random = new Random();
for (int k = 0; k < num; k++) {
int i = random.nextInt(_sources.size() - k);
Object tmp = _sources.get(i);
targetList.add(tmp);
// 取完后,把list size - k 的元素 放到本次取到的index位置
_sources.set(i, _sources.get(_sources.size() - k - 1));
}
return targetList;
}
分析
- 第一種方法,不推薦使用,這里就不說了
- 如果list的實現為ArrayList,那么第三種方法會比第二種好,因為在list移除的時候,實際上發生的數組的復制,有興趣的可以了解ArrayList的實現。
- 如果list的實現為LinkedList,那么第三種方法和第二種方法沒有太多的差別
- 上面的算法實現還可以優化,例如,不用復制一份原數據,直接使用原數據的下標形成新的List,然后對下標進行隨機取,取完后在根據下標去原list中取對應的數據。
- 上面只給了List的實現,那數組的實現呢?方法有兩種:
- 最簡單的是把數組轉換為ArrayList,然后就一樣了
- 根據上面的思路,用數組實現。
各位,如果你有什么更好的想法,也歡迎評論
附源碼
/**
* 獲取不重復下標的元素
*/
public class UniqueCollectionIndex {
public static void main(String[] args) {
int num = 6;
List list1 = new ArrayList();
// 放入A-Z做測試
for (int i = 65; i < 91; i++) {
list1.add((char) i);
}
System.out.println(getListUnique1(list1, num));
System.out.println(getListUnique2(list1, num));
System.out.println(getListUnique3(list1, num));
}
//***** list *******
/**
* <p style="color:red">不推薦使用,可能會發生死循環</p>
* 用set存儲已經取過的下標,每次循環取的時候,判斷是否已經取過
* 如果已經取過就跳過本次循環,一直到取num為止
*
* @param sources 目標list
* @param num 獲取元素的數量
* @return 獲取的list
*/
public static List getListUnique1(List sources, int num) {
if (sources.size() <= num) {
return sources;
}
// 復制一份,以免對原數據造成影響
List _sources = new ArrayList(sources);
List targetList = new ArrayList(num);
Set<Integer> gotIndexs = new HashSet<>(num); // 以獲取的下標
Random random = new Random();
while (gotIndexs.size() < num) {
int i = random.nextInt(_sources.size());
// 已經獲取過了,跳過本次循環
if (gotIndexs.contains(i)) continue;
// 如果沒有獲取過,則放入數據
targetList.add(_sources.get(i));
gotIndexs.add(i);
}
return targetList;
}
/**
* 每一次循環取完數據后,從目標list中移除本次取的數據
*
* @param sources 目標list
* @param num 獲取元素的數量
* @return 獲取的list
*/
public static List getListUnique2(List sources, int num) {
if (sources.size() <= num) {
return sources;
}
// 復制一份,以免對原數據造成影響
List _sources = new ArrayList(sources);
List targetList = new ArrayList(num);
Random random = new Random();
for (int k = 0; k < num; k++) {
int i = random.nextInt(_sources.size());
targetList.add(_sources.get(i));
// 取完后,從目標list內移除該數據
_sources.remove(i);
}
return targetList;
}
/**
* 每一次循環取完數據后,把list size - k -1 的元素 放到本次取到的index位置,
* 下次循環的隨機數最大值為list size - k
*
* @param sources 目標list
* @param num 獲取元素的數量
* @return 獲取的list
*/
public static List getListUnique3(List sources, int num) {
if (sources.size() <= num) {
return sources;
}
// 復制一份,以免對原數據造成影響
List _sources = new ArrayList(sources);
List targetList = new ArrayList(num);
Random random = new Random();
for (int k = 0; k < num; k++) {
int i = random.nextInt(_sources.size() - k);
Object tmp = _sources.get(i);
targetList.add(tmp);
// 取完后,把list size - k 的元素 放到本次取到的index位置
_sources.set(i, _sources.get(_sources.size() - k - 1));
}
return targetList;
}
}