集合学习
Posted gidybzc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了集合学习相关的知识,希望对你有一定的参考价值。
列表相等只需关心元素数据
下边是一个判断列表相等的例子,代码如下:
1 public static void main(String[] args){ 2 ArrayList<String> arr1 = new ArrayList<>(); 3 arr1.add("A"); 4 5 Vector<String> arr2 = new Vector<>(); 6 arr2.add("A"); 7 8 System.out.print("" + arr1.equals(arr2)); 9 }
运行结果为:true
原因分析:二者都是列表(List),实现了List接口,也都继承了AbstractList抽象类,其equals方法是在AbstractList中定义的,其源代码如下:
1 public boolean equals(Object o) { 2 if (o == this) 3 return true; 4 if (!(o instanceof List)) 5 return false; 6 7 ListIterator<E> e1 = listIterator(); 8 ListIterator<?> e2 = ((List<?>) o).listIterator(); 9 while (e1.hasNext() && e2.hasNext()) { 10 E o1 = e1.next(); 11 Object o2 = e2.next(); 12 if (!(o1==null ? o2==null : o1.equals(o2))) 13 return false; 14 } 15 return !(e1.hasNext() || e2.hasNext()); 16 }
可以看到,这里只要求实现List接口。只要List中所有的元素且位置相同,就表明两个List是相等的。
其他的集合类型,如Set、Map等与此相同,也是只关心集合元素。
子列表只是原列表的一个视图
List提供了subList方法,用于返回一个列表的子列表。
1 public static void main(String[] args){ 2 List<String> c = new ArrayList<>(); 3 c.add("A"); 4 c.add("B"); 5 6 List<String> c1 = new ArrayList<>(c); 7 List<String> c2 = c.subList(0, c.size()); 8 c2.add("C"); 9 10 System.out.println("c == c1?" + c.equals(c1)); 11 System.out.println("c == c2?" + c.equals(c2)); 12 }
在上边的例子中,c1是通过ArrayList的构造函数创建的,c2是通过subList方法创建的然后添加了一个元素。运行结果如下:
1 c == c1?false 2 c == c2?true
为什么会有上边的结果呢?来看下subList的源码:
1 public List<E> subList(int fromIndex, int toIndex) { 2 return (this instanceof RandomAccess ? 3 new RandomAccessSubList<>(this, fromIndex, toIndex) : 4 new SubList<>(this, fromIndex, toIndex)); 5 }
subList是由AbstractList实现的,它根据是不是可以随机存取来提供不同的SubList实现方式。由于RandomAccessSubList也是SubList的子类,所以所有的操作都是由SubList类实现的。来看一下SubList类的代码:
class SubList<E> extends AbstractList<E> { private final AbstractList<E> l; private final int offset; private int size; SubList(AbstractList<E> list, int fromIndex, int toIndex) { if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); if (toIndex > list.size()) throw new IndexOutOfBoundsException("toIndex = " + toIndex); if (fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); l = list; offset = fromIndex; size = toIndex - fromIndex; this.modCount = l.modCount; } public E get(int index) { rangeCheck(index); checkForComodification(); return l.get(index+offset); } public void add(int index, E element) { rangeCheckForAdd(index); checkForComodification(); l.add(index+offset, element); this.modCount = l.modCount; size++; } /*其余代码省略,可以在AbstractList.java文件中自行查看*/ }
不难发现,subList返回的SubList类并没有生成一个数组或者链表,它本身只是原列表的一个视图而已,因此所有的修改动作都反映在了原列表上。
使用subList处理足部列表
看一个简单的需求,一个有100个元素,现在要删除索引位置为20~30的元素。可以通过如下的代码实现:
1 public static void main(String[] args) { 2 List<Integer> initData = Collections.nCopies(100, 0); 3 4 ArrayList<Integer> list = new ArrayList<>(initData); 5 6 list.subList(20, 30).clear(); 7 }
但是,这个时候就不要再对原列表进行操作了。来看下边的代码:
1 public static void main(String[] args) { 2 List<Integer> initData = Collections.nCopies(100, 0); 3 4 ArrayList<Integer> list = new ArrayList<>(initData); 5 List<Integer> subList = list.subList(20, 30); 6 list.add(0); 7 8 System.out.print("原列表长度:" + list.size()); 9 System.out.print("子列表长度:" + subList.size()); 10 }
程序运行时,size方法报ConcurrentModificationException。因为subList取出的磊表示原列表的一个视图,原数据集修改了,但是subList取出的子列表不会重新生成,再后面对子列表进行操作时,就会检测到修改计数器与预期的不相同,于是抛出并发修改异常。问题最终还是在子列表提供的size方法的检查上:
public int size() { checkForComodification(); return size; } private void checkForComodification() { if (this.modCount != l.modCount) throw new ConcurrentModificationException(); }
modCount是在SubList子列表的构造函数中赋值的,其值等于生成子列表时原列表的修改次数。在生成子列表后再修改原列表,1.modCount必然比modCount大1,不再相等,于是抛出并发修改异常。
SubList的其他方法也会检查修改计数器。对于子列表操作,子列表的modCount总是跟随原列表进行更新。
可以在生成子列表后,通过 Collections.unmodifiableList 设置原列表为只读状态,在后续的操作中,对原列表只进行读操作,对子列表进行读写操作。防御式编程就是教我们如此做的。
1 public static void main(String[] args) { 2 List<Integer> initData = Collections.nCopies(100, 0); 3 4 List<Integer> list = new ArrayList<>(initData); 5 List<Integer> subList = list.subList(20, 30); 6 //设置list为只读状态 7 list = Collections.unmodifiableList(list); 8 }
集合运算
并集
1 public static void main(String[] args) { 2 3 List<String> list1 = new ArrayList<>(); 4 list1.add("A"); 5 list1.add("B"); 6 7 List<String> list2 = new ArrayList<>(); 8 list2.add("A"); 9 list2.add("B"); 10 11 list1.addAll(list2); 12 }
交集
1 list1.retainAll(list2);
注意:retainAll方法会删除list1中没有在list2中出现的元素
差集
所有属于A但不属于B的元素组成的集合,叫做A与B的差集。
无重复的并集
1 //去除重复元素 2 list2.removeAll(list1); 3 //取并集 4 list1.addAll(list2);
使用shuffle打乱列表
Collections.shuffle(list1);
shuffle方法可以用在一下方面:
- 程序的“伪装”上
如:游戏中打怪,修行,宝物分配的分配策略
- 抽奖程序中
- 安全传输方面
发送端发送一组数据,先随机打乱顺序,加密发送;接收端解密后自行排序即可。
以上是关于集合学习的主要内容,如果未能解决你的问题,请参考以下文章