十、集合
十、集合
ArrayList | 一种可以动态增长和缩减的索引序列 |
LinkedList | 一种可以在任何位置进行高效地插人和删除操作的有序序列 |
ArrayDeque | 一种用循环数组实现的双端队列 |
HashSet | 一种没有重复元素的无序集合 |
TreeSet | —种有序集 |
EnumSet | 一种包含枚举类型值的集 |
LinkedHashSet | 一种可以记住元素插人次序的集 |
PriorityQueue | 一种允许高效删除最小元素的集合 |
HashMap | 一种存储键 / 值关联的数据结构 |
TreeMap | —种键值有序排列的映射表 |
EnumMap | 一种键值属于枚举类型的映射表 |
LinkedHashMap | 一种可以记住键 / 值项添加次序的映射表 |
WeakHashMap | 一种其值无用武之地后可以被垃圾回收器回收的映射表 |
IdentityHashMap | 一种用 = 而不是用 equals 比较键值的映射表 |
Collection
import org.example.Size;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
public class CollectionTest {
Collection collection;
Collection A;
Collection B;
@Before
public void b(){
collection=new ArrayList();
for (int i = 0; i < 5; i++) {
collection.add(i);
}
A=new ArrayList();
for (int i = 0; i < 5; i++) {
A.add(i);
}
B=new ArrayList();
for (int i = 4; i < 7; i++) {
B.add(i);
}
}
/**
* collection 的基本遍历
*/
@Test
public void t1(){
collection.iterator().forEachRemaining( o -> System.out.print(o));
System.out.println();
collection.iterator().forEachRemaining( System.out::print);
System.out.println();
Iterator<Integer> iterator=collection.iterator();
while (iterator.hasNext()){
Integer i=iterator.next();
if (i==2)iterator.remove();
System.out.print(i);
}
System.out.println();
for (Object o :collection){
System.out.print(o);
}
System.out.println();
collection.forEach(o -> {
System.out.print(o);
});
}
/**
* collection 常用方法
*/
@Test
public void t2(){
// 集合大小
System.out.println(collection.size());
// 集合是否为空
System.out.println(collection.isEmpty());
// 集合是否包含对象
System.out.println(collection.contains(3));
// 集合是否包含集合
System.out.println(collection.containsAll(new ArrayList<>()));
//可以看到这个方法改变了集合A中的元素,将存在于集合A中但不存在于集合B中的元素移除。
//如果集合A的大小发生了改变,返回true,即使两个集合完全没有交集,也会返回true。
//如果集合A的大小没有发生改变,返回false,即使两个集合完全相同,也会返回false。
A.retainAll(B);
// 注意这里是 Collection collection,A,B 名字没起好
collection.remove(1);
collection.removeAll(A);
collection.addAll(A);
System.out.println(collection); // [0, 2, 3, 4]
Integer[] integers=new Integer[6];
// 集合转数组
Object[] integers2=collection.toArray(integers);
// Arrays.toString ,按一定格式将数组转化成字符串的
System.out.println(Arrays.toString(integers)); // [0, 2, 3, 4, null, null]
System.out.println(Arrays.toString(integers2));// [0, 2, 3, 4, null, null]
Object[] integers3= collection.toArray();
System.out.println(Arrays.toString(integers3));// [0, 2, 3, 4]
// 清空
collection.clear();
}
}
集合转数组部分订正
// 列表转换数组,
// 数组类型不能强制转换,Object数组 类 和 Integer数组 类 是无法互相转换的
// 这样用的时候不方便操作, (Integer)obejects[0]
Object[] objects=list.toArray();
Integer[] nums2;
// 可以传入一个指定类型的数组 将返回同类型数组
// 如果传入数组长度不够,会新建一个同类型数组并返回
nums2=list.toArray(new Integer[0]);
// 如果传入数组长度够用,则直接填入到这个数组中并返回
nums2=list.toArray(new Integer[10]);
LinkList
import org.junit.Test;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class ListTest {
@Test
public void t1(){
List<Integer> list=new LinkedList<>();
for (int i = 0; i < 5; i++) {
list.add(i);
}
ListIterator<Integer> iterator=list.listIterator();
/// 以下都是 函数生效前 游标位置
// next 返回游标 后面的元素,同时游标右移 之后如果使用 remove/set 操作的是 游标左侧元素
// previous 返回游标 前面的元素,同时游标左移 之后如果使用 remove/set 操作的是 游标右侧元素
// nextIndex返回游标 后面的元素位置
// public int previousIndex() { return nextIndex - 1;}
// previousIndex返回游标 前面的元素位置 = 后面的元素位置-1
System.out.println(iterator.previousIndex());// -1
System.out.println(iterator.nextIndex()); //0
System.out.println(iterator.next()); // 0
iterator.add(7); //
System.out.println(iterator.next());// 1 在添加元素后 iterator previous() 是刚添加的元素,next() 是 刚添加的元素后面的元素
System.out.println(iterator.previous());// 1
iterator.remove();
System.out.println(iterator.next()); // 2
System.out.println(iterator.nextIndex());// 3 0 7 2 | 3 游标在这个位置
iterator.set(6);
iterator.hasNext();
iterator.hasPrevious();
System.out.println("*************");
iterator=list.listIterator();
for (int i = 0; i < 20; i++) {
if (iterator.hasNext()){
System.out.println(iterator.next());
}
}
System.out.println(list.get(1));
// 元素第一次出现的位置 没有找到返回 -1
System.out.println(list.indexOf(4));
// 元素最后一次出现的位置 没有找到返回 -1
System.out.println(list.lastIndexOf(4));
LinkedList<Integer> list2=new LinkedList<>();
list2.addFirst(1);
list2.addLast(3);
list2.getFirst();
list2.getLast();
list2.removeFirst();
list2.removeLast();
}
}
ArrayList
Vector 也可以实现动态数组
Vector 所有方法都是同步的,耗时
ArrayList 所有方法都是不同步的
HashSet
-
桶:收集具有相同散列值的数据结构,具有相同散列值得放在一个桶里
-
桶数:桶的数目,通常将桶的数目设置为预计元素个数的 75%~150%
-
填装因子:(默认为 0.75) 当表中超过 75% 的位置已经被填充,就会用双倍的桶数进行再散列
-
再散列:重新创建一个桶数更多的表,将原表元素插入新表,并舍弃旧的表
Map 的 key 是唯一的,从 HashSet 的源码中可以看到,HashSet 实际上是使用了 HashMap 的 key 作为元素
注释中说明了默认的 桶数为 16 ,填装因子为 0.75
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
常用方法
HashSet<String> hashSet=new HashSet<>();
// public HashSet(Collection<? extends E> c) {
// 将集合全部元素添加到散列集
new HashSet<String>(new ArrayList<String>());
// 设置桶数
new HashSet<String>(16);
// 设置桶数和填装因子
new HashSet<String>(16,0.75f);
// 添加元素
hashSet.add("zs");
hashSet.add("ls");
hashSet.add("zs");
// 是否包含元素
hashSet.contains("zs");
hashSet.remove("zs");
//
// 对于要存于 散列集的对象要重写 hashCode() 方法 和 equals() 方法,
// 并且要保证 若果 x.equals(y) 为 true 那么 x.hashCode()==y.hashCode 为true
//
"zs".hashCode();
hashSet.iterator().forEachRemaining(System.out::println);
特点:
- 集合元素可以是null,但只能放入一个null
- 不是同步的
TreeSet
// TreeSet 中的元素 在遍历时,是按顺序排列的,当前是使用 红黑树 实现的
// TreeSet 实现了 SortedSet 接口
// 使用树集 元素必须实现 Comparable 接口,(元素不能比较的话,没办法排序的),或者 new 的时候传入比较器
TreeSet<String> set=new TreeSet<>();
set.add("john");
set.add("bob");
set.add("carlos");
set.add("bob");
set.add("a");
set.add("z");
// 遍历
for (String s : set) {
System.out.println(s);
}
set.iterator().forEachRemaining(System.out::println);
// 常用方法
set.first();
set.last();
// NavigableSet 接口的方法
// 返回 比 bz 大的元素中 最小的
System.out.println(set.higher("bz")); // carlos
// 返回 比 bz 小的元素中 最大的
System.out.println(set.lower("bz")); // bob
// 返回 比 bob 大或等于的元素中 最小的
System.out.println(set.ceiling("bob"));
// 返回 比 bob 小或等于的元素中 最大的
System.out.println(set.floor("bob"));
// 删出最小元素并返回
set.pollFirst();
// 删出最大元素并返回
set.pollLast();
System.out.println("*********");
// descendingIterator() 返回一个反向迭代器,用于逆序遍历
set.descendingIterator().forEachRemaining(System.out::println);
Queue
-
双端队列 Deque
左往右的队列操作,右往左也支持
-
优先级队列 PriorityQueue
可以以任意顺序插入,但是始终是按顺序检索
使用数据结构 堆(heap):一个可以自我调整的二叉树
常用方法
Queue<Integer> queue=new ArrayDeque<>();
queue.add(999);
// 以下方法,每组 前面的 出错抛出异常,后面的 返回 false 或者 null
// 添加元素 ,队列满了则无法添加
queue.add(1); //抛出异常
queue.offer(2); // 返回 false
// 移除并返回头部元素
queue.remove();
queue.poll();
// 返回头部元素,但不删除
queue.element();
queue.peek();
// 双端队列
// public interface Deque<E> extends Queue<E> {
// ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>
Deque<Integer> deque=new ArrayDeque<>();
deque.add(999);
// 头插 和 尾插
deque.addFirst(1); // 出错抛出异常
deque.addLast(2);
deque.offerFirst(3); // 出错返回 false
deque.offerLast(4);
// 移除并返回头/尾部元素
deque.removeFirst();
deque.removeLast();
deque.pollFirst();
deque.pollLast();
// 返回头/尾部元素,但不删除
deque.getFirst();
deque.getLast();
deque.peekFirst();
deque.peekLast();
// 设置初始容量,无参构造器 默认为 16
// ArrayQueue 在满了的时候能自动扩充容量
Queue<Integer> queue2=new ArrayDeque(8);
// 优先级队列
// 元素可以按任意顺序插入,但是总是按照排序的顺序进行检索
// 使用了 数据结构 堆 (heap 一个可以自我调整的二叉树)
// PriorityQueue<E> extends AbstractQueue<E>
// AbstractQueue<E> extends AbstractCollection<E> implements Queue<E>
Queue<Integer> queue3=new PriorityQueue<>();
queue3.add(9);
queue3.add(8);
queue3.add(2);
queue3.add(7);
System.out.println(queue3); // [2, 7, 8, 9]
HashMap
Map<Integer,String> map=new HashMap<>();
map.put(1,"1zs");
map.put(2,"2ls");
String name;
name=map.get(1);
// 获取失败时,用 无名氏 当作默认值返回
name=map.getOrDefault(99,"无名氏");
map.put(2,"2ls cover");
// void putAll(Map<? extends K, ? extends V> m);
map.putAll(new HashMap<>());
boolean b;
// 查看 是否包含 键
b=map.containsKey(1);
// 查看 是否包含 值
b=map.containsValue("2ls");
// 对map中每组 键值对 都执行 某操作
map.forEach((k,v)->{
System.out.println("key "+ k+" : "+v);
});
new HashMap<>();
// 初始化时设置桶数,和 Set 相同
new HashMap<>(16);
// 初始化时设置桶数和填装因子
new HashMap<>(16,0.75f);
TreeMap
能自动按键排序的 Map
TreeMap<Integer,String> treeMap=new TreeMap<>();
// public TreeMap(Comparator<? super K> comparator) {
// 注意 是 Comparator 不是 Comparable
// 是 tor ! 不是 able!
// 可以传入一个比较器,作为 key 的比较方法,可以用 lambda 表达式写法,也可以 匿名类 或者写一个实现接口的类
new TreeMap<Integer,String>((a,b)->{return 1;});
treeMap.put(3,"3ww");
treeMap.put(1,"1zs");
treeMap.put(2,"2ls");
treeMap.put(2,"2ls cover");
// SortedMap 接口的方法
// 返回最小 key 和 最大 key
treeMap.firstKey();
treeMap.lastKey();
// 当原先 key 对应 value 不存在时 才放入新 value
treeMap.putIfAbsent(1,"1 putIfAbsent");
// 参数 1 key
// 参数 2 新值
// 参数 3 处理旧值和新值 的过程
// get(key) 为 null 时,直接将参数 2 作为新value
// 不为空的时候 用参数 3 处理 原value 和 参数 2 ,结果作为新value
// 如果参数 3 处理结果为 null 则移除value 即 remove(key)
// 不为空则 put(key ,newValue)
// 详情见源码
treeMap.merge(1,"merger",String::concat); // 1:1zsmerger
treeMap.merge(4,"--merger",(a,b)->{return a+b+"lambda";});// 4:--merger
// 将 key 和 value 做一定处理 作为新的 value
// 及时 get(key) 为空也会处理
// 处理结果为 null 则会移除 remove(key)
// 不为空则 put(key ,newValue)
treeMap.compute(3,(k,v)->{return k+v+"lambda";}); // 3:33wwlambda
treeMap.compute(9,(k,v)->{ return k+v+"lambda";});// 9:9nulllambda
// 当 key 对应值 不为 null 时才做处理,否则直接返回
treeMap.computeIfPresent(10,(k,v)->{ return k+v+"computeIfPresent";});
// 当 key 对应值 为 null 时才做处理
treeMap.computeIfAbsent(11,(k)->{ return k+"computeIfPresent";});
// 将所有映射 都用函数处理
treeMap.replaceAll((k,v)->{ return v;});
treeMap.forEach((k,v)-> System.out.println(k+":"+v));
System.out.println(null+"-hello"); // 输出 null-hello
Map映射视图
map.keySet();
map.values();
map.entrySet();
对 Map 做映射方便做一些操作,有点像 SQL 的视图
对三种映射视图通过 iterator 做 remove() 操作,都会同时影响到原 map
若能修改视图,则也会使 原来的 map 做出相应的修改
但是都不能使用 add() 方法,会抛异常
Map<Integer,String> map=new HashMap<>();
map.put(2,"2ls");
map.put(3,"3ww");
map.put(1,"1zs");
map.put(4,"4zl");
// 返回 键 集
Set<Integer> integers = map.keySet();
// 返回 值 的集合
Collection<String> values = map.values();
// 返回 键值组合的结构 Entry 的集
Set<Map.Entry<Integer, String>> entries = map.entrySet();
// 1=1zs
// 2=2ls
// ··········
// 输出结果如上, toString() 方法会将 key 和 value 中间以等号拼接
// static class Node<K,V> implements Map.Entry<K,V> {
// public final String toString() { return key + "=" + value; }
entries.forEach(System.out::println);
// 在键集视图上,用迭代器 remove() 方法删除键,会删除原来 map 中对应的 键和值
Iterator iterator=integers.iterator();
while (iterator.hasNext()){
Integer i= (Integer) iterator.next();
if (i==2){
iterator.remove();
}
}
// 在值集合视图上,用迭代器 remove() 方法删除键,会删除原来 map 中对应的 键和值
Iterator<String> iterator2 = values.iterator();
while (iterator2.hasNext()){
String next = iterator2.next();
if ("1zs".equals(next)){
iterator2.remove();
}
}
// 在键值集视图上,用迭代器 remove() 方法删除键,会删除原来 map 中对应的 键和值
// 修改键对应的值,会使原来的 map 被相应的修改
Iterator<Map.Entry<Integer, String>> iterator3 = entries.iterator();
while (iterator3.hasNext()){
Map.Entry<Integer, String> next = iterator3.next();
if (3==next.getKey()){
next.setValue("通过 Entry 被修改了");
}
if (4==next.getKey()){
iterator3.remove();
}
}
// map 中的 1 2 4 都被 被删除了
// 键 3对应的值被修改为 : 3通过 Entry 被修改了
map.forEach((k,v)-> System.out.println(k+v));
// 三种映射视图都不能使用 add() 方法,会抛异常
//integers.add(5);
//values.add("5");
//entries.add(null);
weakHashMap弱散列映射
能与垃圾回收器协同工作的 Map
// WeakHashMap 使用弱引用保存键
// 当对键的唯一引用,来自散列条目时,这一数据结构将与垃圾回收器协同工作,一起删除键值对
WeakHashMap<Integer, String> weakHashMap = new WeakHashMap<>();
// WeakHashMap 不是线程安全的 ,在并发场景下使用,可以使用 Collections.synchronizedMap(weakHashMap);
// Map<Integer, String> map = Collections.synchronizedMap(weakHashMap);
weakHashMap.put(1,"1zs");
weakHashMap.put(2,"2zs");
LinkedHashSet LinkedHashMap
能按顺序(插入顺序或受影响顺序)遍历的 HashSet 和 HashMap
// LinkedHashMap 会记住数据插入的顺序,可以按插入顺序迭代
LinkedHashMap<Integer,String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put(1,"1zs");
linkedHashMap.put(3,"3zs");
linkedHashMap.put(2,"2zs");
linkedHashMap.put(4,"4zs");
// 1 3 2 4
linkedHashMap.forEach((k,v)->{System.out.println(k+" : "+v);});
// 第三个参数 accessOder 默认为false,若设置为true ,
// 则每当 调用get 或 put ,会将受影响的条目从当前位置移除,放置到条目链表的尾部
// 这种特性可以应用于高速缓存的最近最少用原则,最前面的元素说明最近没有访问,有新数据要加入缓存可以移除靠前面的元素
linkedHashMap = new LinkedHashMap<Integer,String>(16,0.75f,true);
linkedHashMap.put(1,"1zs");
linkedHashMap.put(3,"3zs");
linkedHashMap.put(2,"2zs");
linkedHashMap.put(4,"4zs");
linkedHashMap.get(1);
linkedHashMap.get(2);
linkedHashMap.get(3);
linkedHashMap.get(4);
// 1 2 3 4
linkedHashMap.forEach((k,v)->{System.out.println(k+" : "+v);});
// 会记住插入顺序的 集,会以插入顺序进行遍历
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("1zs");
linkedHashSet.add("3zs");
linkedHashSet.add("2zs");
linkedHashSet.add("4zs");
linkedHashSet.contains("3zs");
// 1 3 2 4
linkedHashSet.forEach(System.out::println);
EnumSet EnumMap
针对枚举类型作为 集的元素 或者 Map 的 key 时
一种高效的 Set 和 Map
// EnumSet 是 枚举类型 的集的高效实现
// 没有 public 构造方法,需要使用静态工厂方法构造集
EnumSet<Size> sizes ;
// 返回一个包含所有枚举值的集
sizes= EnumSet.allOf(Size.class);
// 返回一个空集
sizes= EnumSet.noneOf(Size.class);
// 返回一个从 x 到 x 的枚举值的集
sizes=EnumSet.range(Size.SMALL,Size.LARGE);
// 返回一个包含 参数 1 ,参数 2,参数 3 ··· 的枚举值得值
sizes=EnumSet.of(Size.SMALL,Size.LARGE);
sizes.forEach(System.out::println);
// EnumMap 是一个键类型是枚举类型的 map,它直接高效的用数组实现
// 构造时需要指定键的枚举类型
EnumMap<Size, Object> enumMap = new EnumMap<>(Size.class);
IdentityHashMap
根据对象的内存来判断 key 是否一致的 Map
// IdentityHashMap
// 这个类中键的散列值是使用 System.identityHashCode(Object o) 计算,(一种根据对象的内存地址来计算散列码的方式)
// 而不是 hashCode()
// 对象比较的时候,使用的是 == 而不是 equals
String a="abcd";
String b= "ab";
// 避免 编译器优化,使 a,b 有不同的内存地址
if (a.equals("abcd")){
b+="cd";
}
System.out.println(a==b); //false
System.out.println(a.equals(b));// true
System.out.println(System.identityHashCode(a)==System.identityHashCode(b));//false
IdentityHashMap<String,Integer> identityHashMap=new IdentityHashMap<>();
identityHashMap.put(a,1);
identityHashMap.put(b,2);
identityHashMap.forEach((k,v)->{
System.out.println(k+":"+v);
});
/*
输出结果
false
true
false
abcd:1
abcd:2
*/
视图与包装器
类似 HashMap 的 keySet() values() 还有一些其他视图
轻量级集合包装器
- 数组包装成 List : Arrays.asList()
// List = Arrays.asList() 可以修改数据,不能改变数组大小(不能对list添加删除)
String[] strings={"1zs","2ls","3ww"};
List<String> list = Arrays.asList(strings);
list.set(0,"1-set");
list.get(0);
// 输出 1-set 2ls 3ww
list.forEach(s -> {System.out.println(s);});
// [1-set, 2ls, 3ww]
System.out.println(Arrays.toString(strings));
// 任何改变 数组大小的操作如 add remove 都会抛出异常
//list.add("ss");
- 单一元素
// List = Collections.nCopies(重复次数,要重复的Object)
// 不允许修改数据
List<String> list2 = Collections.nCopies(15, "你好");
// 输出 15 组你好
list2.forEach(System.out::print);
System.out.println();
// 不允许修改数据
//list2.set(2,"不好");
// 不允许改变
//list2.add("xx");
// 获得一个单一的元素集、列表、映射
Set<String> singleton = Collections.singleton("1");
List<String> singletonList = Collections.singletonList("1");
Map<Integer, String> singletonMap = Collections.singletonMap(1, "1");
子范围
-
List 子范围
获得一个范围内的子列表
// List 子范围 List.subList(int fromIndex, int toIndex);
// 可以将任何操作应用到子范围(包括 remove add)
List<Integer> list3=new LinkedList<>();
list3.add(1);
list3.add(2);
list3.add(3);
list3.add(4);
// 返回一个索引从 参数 1 (包括此索引) ,到 参数 2 (不包括此索引) 的 List
// 用函数定义域理解就是 [ 1 ,3 )
List<Integer> list4 = list3.subList(1, 3);
list4.clear(); // list3:[1, 4]
list4.add(6); // list3:[1, 6, 4]
System.out.println(list3);
-
SortedSet 子范围
获得一个范围内的子元素集
// SortedSet 子范围
// 可以在 子范围内进行操作
SortedSet<String> sortedSet = new TreeSet<>();
sortedSet.add("az");
sortedSet.add("za");
sortedSet.add("gg");
// 返回 大于等与参数1 小于 参数2 的元素子集
// ["bb" ,"qq")
SortedSet<String> sortedSet2 = sortedSet.subSet("bb", "qq");
System.out.println(sortedSet2);// [gg]
sortedSet2.remove("gg"); // sortedSet:[az, za]
// 可以在子范围内操作 添加
sortedSet2.add("qa"); // sortedSet: [az, qa, za]
// "zz">"qq" 添加了超出子范围的 数据 会抛出异常
//sortedSet2.add("zz");
System.out.println(sortedSet);
// 返回 小于 "qa" 的元素子集
sortedSet.headSet("qa"); // [az]
// 返回 大于等于 "az" 的元素子集
sortedSet.tailSet("az"); // [az, qa, za]
不可修改的视图、同步视图、受查视图
- 同步视图
转换获得一个具有同步访问方法的 集合 - 受查视图
插入时检查 插入对象是否为给定类
// 不可修改的视图
// Collections.unmodifiableCollection
// Collections.unmodifiableList
// Collections.unmodifiableSet
// Collections.unmodifiableSortedSet
// Collections.unmodifiableNavigableSet
// Collections.unmodifiableMap
// Collections.unmodifiableSortedMap
// Collections.unmodifiableNavigableMap
Collection<String> strings1 = Collections.unmodifiableCollection(list);
System.out.println(strings1);
// 同步视图
// 转换获得一个具有同步访问方法的 集合
Collections.synchronizedCollection(list);
// Collections.synchronizedList()
// 其他方法同上省略 ········
// 受查视图
// 插入时检查 插入对象是否为给定类
// 如下代码
ArrayList<String> list8=new ArrayList<>();
ArrayList list9= list8;
list9.add(new Date());
// 在这里会报错 ,但是实际上出错的位置是在 add 处,无法正确检测到出错位置
// String s= (String) list9.get(0);
// 获得一个安全的列表视图,在 add 时候就会检查插入对象是否为给定类
List<String> safeList=Collections.checkedList(list8,String.class);
// Collections.checkedCollection()
// 其他方法同上省略 ········
常用算法
- 排序和混排
List<Integer> list=new ArrayList<>();
list.add(7);
list.add(9);
list.add(2);
list.add(3);
System.out.println(list);
// 排序 默认升序
Collections.sort(list);
System.out.println(list); // [2, 3, 7, 9]
// 自定义比较器
list.sort((a,b)->{ return b.compareTo(a);}); // [9, 7, 3, 2]
list.sort(Integer::compareTo); // [2, 3, 7, 9]
// public int compare(Comparable<Object> c1, Comparable<Object> c2) {return c2.compareTo(c1);}
// 传入了一个 用 c2.compareTo(c1) 实现 compare 方法的比较器,因此排出来就是降序的了
// 降序排列
list.sort(Comparator.reverseOrder());
System.out.println(list); // [9, 7, 3, 2]
// 将原数据转化成 double 在做逆序排列
// Integer::doubleValue 是传入的 转化成 double 数据的方法
list.sort(Comparator.comparingDouble(Integer::doubleValue).reversed());
System.out.println(list);
// 根据员工的工资 逆序排列员工
//staff.sort(Comparator.compari ngDoubl e(Empl oyee::getSalary) . reversedO)
// 打乱数组顺序
Collections.shuffle(list);
// 集合类库中使用的是归并排序,可以保证 通过比较器认为相同的两个元素,会按原有顺序排列
// 比如 已经按照总分排好的 学生成绩表,在按照数学成绩排序时,数学成绩相同的学生,总分高的仍会排在前面
-
二分查找
使用二分查找必须先排序,否则会得出错误的结果
二分查找 对支持随机访问的数据结构才有意义,如果提供的是 LinkedList 将自动变为线性查找
List<Integer> list=new ArrayList<>();
Integer[] nums={9,7,6,2,6,4,7,8,6,3,4};
//for (Integer num : nums) { list.add(num);}
list=Arrays.asList(nums);
// 使用二分查找必须先排序,否则会得出错误的结果
Collections.sort(list);
System.out.println(list); // [2, 3, 4, 4, 6, 6, 6, 7, 7, 8, 9]
// 返回找到元素的下标,如果元素重复,返回的是根据二分查找算法首次找到元素的下标,而不是重复元素的首个下标
// 查找失败时返回负数 i ,-i-1 为查找元素应插入位置
int index = Collections.binarySearch(list, 6);
System.out.println(index); // 5
// 二分查找 对支持随机访问的数据结构才有意义,如果提供的是 LinkedList 将自动变为线性查找
- Collections 提供的一些简单算法
List<Integer> list=new ArrayList<>();
Integer[] nums={9,7,6,2,6,4,7,8,6,3,4};
//list=Arrays.asList(nums);
for (Integer num : nums) { list.add(num);}
int i;
i=Collections.max(list);
i=Collections.min(list);
// Collection.copy 是浅拷贝
// Collection.copy 方法要求目标列表的 size 必须大于 源列表的 size ,否则直接抛出IndexOutOfBoundsException
// list 的size() 只有在 add 和 remove 的时候才会发生改变,所以先用空对象填充
List<Integer> newList=new ArrayList(Arrays.asList(new Object[list.size()]));
Collections.copy(newList,list);
System.out.println(newList);
// boolean List.addAll(Collection<? extends E> c);
// 把一个集合的所有元素添加到 list 中 ,浅拷贝 添加的是引用
List list3=new ArrayList();
list3.addAll(list);
// 将列表中所有位置,都用相同的元素填充
Collections.fill(newList,2); // [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
Collections.replaceAll(list,2,-2); // [9, 7, 6, -2, 6, 4, 7, 8, 6, 3, 4]
Integer[] sub={2,2};
List subList=Arrays.asList(sub);
// 查找 子列表 首次出现时的索引
i=Collections.indexOfSubList(newList,subList); // 0
// 查找 子列表 最后一次出现时的索引
i=Collections.lastIndexOfSubList(newList,subList);// 9
// 交换 给定两个索引的元素 位置
Collections.swap(list,0,1); // [7, 9, 6, -2, 6, 4, 7, 8, 6, 3, 4]
// 逆置 列表中的元素
Collections.reverse(list); // [4, 3, 6, 8, 7, 4, 6, -2, 6, 9, 7]
// 旋转列表中的元素,将所有元素向右循环移动 d(2) 个位置, 即 i 移动到 (i+d)%list.size() 位置
Collections.rotate(list,2); // [9, 7, 4, 3, 6, 8, 7, 4, 6, -2, 6]
// 元素出现的次数
i=Collections.frequency(list,6); // 3
// 两个集合是否有没有交集 ,两个集合没有共同元素 返回 true
boolean b=Collections.disjoint(list,newList);
Collection<Integer> collection=list;
// 条件删除
collection.removeIf(t -> { return t<0?true:false;}); // [9, 7, 4, 3, 6, 8, 7, 4, 6, 6]
// 自定义对列表所有元素进行操作
list.replaceAll( t->{ return -t;}); // [-9, -7, -4, -3, -6, -8, -7, -4, -6, -6]
System.out.println(list);
- 按批量作
List<Integer> list=new ArrayList<>();
Integer[] nums={9,7,6,2,6,4,7,8,6,3,4};
//list=Arrays.asList(nums); // 涉及到 add remove 不能使用!
for (Integer num : nums) { list.add(num);}
Collection collection=list;
Collection collection2=new ArrayList();
collection2.add(6);
// 移除所有 出现在 collection2 的元素
collection.removeAll(collection2); // [9, 7, 2, 4, 7, 8, 3, 4]
// 移除所有 没有出现在 collection2 的元素
collection.retainAll(collection2); // []
// 添加所有 collection2 的元素,浅拷贝
collection.addAll(collection2); // [6]
System.out.println(collection);
- 集合和数组转换
Integer[] nums={1,3,5,4,8};
// 数组转换列表,详情参考视图和包装
List<Integer> list=Arrays.asList(nums);
// 列表转换数组,
// 数组类型不能强制转换,Object数组 类 和 Integer数组 类 是无法互相转换的
// 这样不方便操作, (Integer)obejects[0]
Object[] objects=list.toArray();
Integer[] nums2;
// 可以传入一个指定类型的数组 将返回同类型数组
// 如果传入数组长度不够,会新建一个同类型数组并返回
nums2=list.toArray(new Integer[0]);
// 如果传入数组长度够用,则直接填入到这个数组中并返回
nums2=list.toArray(new Integer[10]);
nums2=list.toArray(new Integer[list.size()]);