Java之Collections使用

本文主要介绍了Java中Collections的查询操作,特别是二分查找的方法,详细解析了其工作原理和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Collections提供了哪些操作呢?

一、查询操作
1.查找元素
binarySearch(List<? extends Comparable<? super T>> list, T key)
binarySearch(List<? extends T> list, T key, Comparator<? super T> c)

看方法名就知道它是用的二分查找

注意:

集合中元素是没有经过排序,那么结果是未知的
另外,排序的元素必须实现排序接口(Comparable)或者比较器接口(Comparator)。

List<String> str = new ArrayList<String>();
str.add("b");
str.add("c");
str.add("a");

System.out.println(Collections.binarySearch(str, "a")); //返回-1, 即没有找到

现在调整一下顺序:

str.add("b");
str.add("a");
str.add("c");

System.out.println(Collections.binarySearch(str, "a")); //返回1
2.集合中最大/最小元素
 // 查找元素最大值
 <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)
                                        <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)

// 查找元素最小值
<T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)
<T> T min(Collection<? extends T> coll, Comparator<? super T> comp)

注意:

排序的元素要实现排序接口(Comparable)或者比较器接口(Comparator)哦

\\ 基本类型
str.add("b");
str.add("a");
str.add("c");

System.out.println(Collections.max(str)); //c
System.out.println(Collections.min(str)); //a
\\ 自定义类
public class Person implements Comparable<Person>{ //实现Comparable接口

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Person person){ //实现compareTo方法
        return this.age - person.age;
    }
}
Person p1 = new Person("Zzzz", 40);
Person p2 = new Person("Kkkk", 30);
Person p3 = new Person("Mmmm", 50);

ArrayList<Person> al = new ArrayList<Person>();
al.add(p1);
al.add(p2);
al.add(p3);

System.out.println(Collections.max(al).getName()); //Mmmm
System.out.println(Collections.min(al).getName()); //Kkkk
3.集合中的是否存在子集合
static int indexOfSubList(List<?> source, List<?> target) //子集合是首次出现的索引
static int lastIndexOfSubList(List<?> source, List<?> target) //子集合最后一次出现的索引

根据源码解释,它使用的是暴力匹配的方式。

4.集合中是否有相同的元素,没有返回true
boolean disjoint(Collection<?> c1, Collection<?> c2)

Source code ~

//关键实现
for (Object e : iterate) {
    if (contains.contains(e)) {
        // Found a common element. Collections are not disjoint.
         return false;
     }
 }
5.返回元素在集合中出现的次数
int frequency(Collection<?> c, Object o)

Source code ~

int result = 0;
if (o == null) {
    for (Object e : c)
        if (e == null)
            result++;
} else {
    for (Object e : c)
        if (o.equals(e))
            result++;
}
return result;
二、修改集合结构
1.对集合的元素排序
<T extends Comparable<? super T>> void sort(List<T> list)
                              <T> void sort(List<T> list, Comparator<? super T> c)

Note:

排序的元素是否实现排序接口(Comparable)或者比较器接口(Comparator)

2.反转集合元素顺序
static void reverse(List<?> list)

它的源码主要展示了当集合的元素小于REVERSE_THRESHOLD 时使用随机访问,在大于REVERSE_THRESHOLD时使用迭代器访问。

public static void reverse(List<?> list) {
 int size = list.size();
 if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) {
     for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
         swap(list, i, j);
 } else {
     ListIterator fwd = list.listIterator();
     ListIterator rev = list.listIterator(size);
     for (int i=0, mid=list.size()>>1; i<mid; i++) {
         Object tmp = fwd.next();
         fwd.set(rev.previous());
         rev.set(tmp);
     }
 }
3.打乱集合顺序
static void shuffle(List<?> list)
static void shuffle(List<?> list, Random rnd)

—>关于Collections.shuffle()方法

4.交换集合两个元素的顺序

直接看源码吧~

public static void swap(List<?> list, int i, int j) {
        final List l = list;
        l.set(i, l.set(j, l.get(i)));
    }
5.以指定元素填充集合
static <T> void fill(List<? super T> list, T obj)
ArrayList<String> strList = new ArrayList<String>();
strList.add("b");
strList.add("c");
strList.add("d");
forList(strList); //封装了foreach的方法  

Collections.fill(strList, "z");
forList(strList); //封装了foreach的方法  

输出:
b c d
z z z
6.集合元素移动
void rotate(List<?> list, int distance)
ArrayList<String> strList = new ArrayList<String>();
strList.add("b");
strList.add("c");
strList.add("d");

Collections.rotate(strList, 1); //所有元素向右移动一个位置
forList(strList); //封装了foreach的方法  

Collections.rotate(strList, -2); // 所有元素向左移动两个位置
forList(strList); //封装了foreach的方法  

输出:
d b c
c d b
7.以元素a替换集合所有的元素b
static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
ArrayList<String> strList = new ArrayList<String>();
strList.add("b");
strList.add("c");
strList.add("d");
strList.add("c");

Collections.replaceAll(strList, "c", "z");
forList(strList); //封装了foreach的方法  

输出:
b z d z
8.添加指定元素到指定集合末尾
 static <T> boolean addAll(Collection<? super T> c, T... elements)
ArrayList<String> strList = new ArrayList<String>();
strList.add("b");

Collections.addAll(strList, "c", "z");
forList(strList);//封装了foreach的方法  

输出:
b c z

源码~

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
	boolean result = false;
	for (T element : elements)
	    result |= c.add(element);
	return result;
    }

|= 与 += 的形式是一样的,"|"差不多是逻辑或的意思,只要有一方为真结果就为真。
——>Java中布尔类型操作&=,|=与^=的使用

9.替换集合的前n个元素
static <T> void copy(List<? super T> dest, List<? extends T> src)
List<String> strList = new ArrayList<String>();
strList.add("b");
strList.add("c");
strList.add("d");

List<String> copyList = new ArrayList<String>();
Collections.copy(copyList, strList); // 将集合元素复制到一个空集合
forList(copyList); //封装了foreach的方法

输出:java.lang.IndexOutOfBoundsException: Source does not fit in dest

Why?

让我们看看导致这一问题的源码:

if (srcSize > dest.size())
   throw new IndexOutOfBoundsException("Source does not fit in dest");

也就是说并不能将原集合(srcList)元素直接复制到一个空集合(destList),使用条件 destList.siz() >= srcList.size()

// 修改一下copyList
List<String> copyList = new ArrayList<String>();
copyList.add("e");
copyList.add("e");
copyList.add("e");
copyList.add("e");

Collections.copy(copyList, strList);
forList(copyList);
输出:
b c d e // 替换了前3个元素

That means, 使用该方法会将目标集合前src.size()个元素替换为原集合的元素

// 源码
for (int i=0; i<srcSize; i++)
    dest.set(i, src.get(i));

Note: 要复制集合元素到一个空集合, 可以考虑使用

// 集合继承自Collection的
addAll(Collection<? extends E> c);

// Collections
static <T> boolean addAll(Collection<? super T> c, T... elements) // T...elements 可变参数
tip:
从Java 5版本开始,方法的参数如果是相同类型的变量列表(称为可变参数),则可以使用一种特殊的语法,比如:
public void find(String ... elements) {
    // Some implementation here
}

编译器会转换可变参数为对应类型的一个数组
三、返回新的集合/视图
1.返回指定集合的不可修改视图
static <T> List<T> unmodifiableList(List<? extends T> list)
// 示例
 List<String> strList = new ArrayList<String>();
 strList.add("b");
 strList.add("c");
 strList.add("d");

 List<String> unMoList =Collections.unmodifiableList(strList);
 unMoList.add("d");
 
 输出:
 java.lang.UnsupportedOperationException

部分源码展示:

public E set(int index, E element) {
            throw new UnsupportedOperationException();
}
public void add(int index, E element) {
    throw new UnsupportedOperationException();
}
public E remove(int index) {
    throw new UnsupportedOperationException();
}

可以看到,涉及到修改集合结构的操作是不被允许的. 类似的视图还有 unmodifiableMap, unmodifiableSet…

参考资料:
由Collections.unmodifiableList引发的重构

2.返回由指定集合为参数的同步(线程安全)集合 //todo 等待更新

这个涉及到线程安全和并发,是我的姿势盲区。等我GET到以后,我会回来更新的~

涉及到的视图有synchronizedList*, synchronizedMap(Map<K,V> m), synchronizedSet(Set s)…

3.返回指定集合的动态类型安全视图(debug)
Collection<E> checkedCollection(Collection<E> c, Class<E> type)

根据源码解释,这个方法可以用于检测将错误类型的元素插入集合时引发的ClassCastException。这个错误一般发生在传递引用的时候。

{@code
    Collection<String> c = new HashSet<String>();
 }
may be replaced temporarily by this one:
 {@code
     Collection<String> c = Collections.checkedCollection(
         new HashSet<String>(), String.class);
 }
// 示例
List<Integer> list = new ArrayList<Integer>();
list.add(3);

List obj = list; //传递引用
// 编译时不会报错
obj.add("dd"); 

int num = 0;
for(Integer a : list ){
    num += a;
}

输出:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at test.Test.main(Test.java:32)

Now 我们修改一下

// 修改创建list部分
List<Integer> list = Collections.checkedList(new ArrayList<Integer>(), Integer.class);

运行结果:
java.lang.ClassCastException: Attempt to insert class java.lang.String element into collection with element type class java.lang.Integer
...
at test.Test.main(Test.java:29)

对比运行结果可以知道该方法能够发现最开始发生错误的地方。Of cource, 修改错误后要把集合初始化的地方改回去

其它集合类型也有对应的方法检查:
checkedMap(Map<K, V> m,Class keyType,Class valueType) , checkedSet(Set s, Class type)…

4.返回不可变空集合与迭代器(代码优化)
static final <T> List<T> emptyList()
static final <T> Set<T> emptySet()
static final <K,V> Map<K,V> emptyMap()

参考资料:
—>Collections.emptyList()与高并发
—>不可变空集合与迭代器(Immutable empty collections and iterators)

5.返回一个不可变长度为1的集合,该集合只包含指定的对象。
// Returns an immutable list containing only the specified object.
static <T> List<T> singletonList(T o)
static <T> Set<T> singleton(T o)
static <K,V> Map<K,V> singletonMap(K key, V value)

调用size()方法只会返回1

public int size(){return 1;}

我查了资料后发现有一个使用场景是, 当你调用的方法需要的参数是一个集合, 但是只用到一个元素时。Like:

// 示例:从集合中删除空元素
List<String> strList = new ArrayList<String>();
strList.add("b");
strList.add(null);
strList.add("d");
forList(strList); // 封装foreach的方法

System.out.println("=======");

strList.removeAll(Collections.singleton(null)); 
forList(strList);
}

输出:
b null d 
=======
b d 
6.返回由n个同一对象组成的不可变列表。(nnCopies)
public static <T> List<T> nCopies(int n, T o)
List<String> s = Collections.nCopies(4, "Zzz");
forList(s); // 封装foreach的方法

输出:
Zzz 
Zzz 
Zzz 
Zzz 

这方法有啥用啊?(尼克杨问号脸)

源码注释提供:

this method is useful in combination with the <tt>List.addAll</tt> method to grow lists.

7.返回一个反序的比较器
<T> Comparator<T> reverseOrder()
static <T> Comparator<T> reverseOrder(Comparator<T> cmp)

没啥好说的, 应该是为了配合sort方法使用排序的。

8.返回指定集合上的枚举
static <T> Enumeration<T> enumeration(final Collection<T> c)

相当于早期版本的Iterator吧, 来段代码感受下:

List<String> strList = new ArrayList<String>();
strList.add("b");
strList.add(null);
strList.add("d");

Enumeration<String> en = Collections.enumeration(strList);
while(en.hasMoreElements()){
    String str = en.nextElement();
    System.out.println(str);
}

输出:
b
null
d

参考资料:
关于Enumeration和Iterator的区别

9.返回一个数组列表,其中包含由指定枚举返回的元素
static <T> ArrayList<T> list(Enumeration<T> e)
10.提供了一个和Map实现相对应的Set实现
static <E> Set<E> newSetFromMap(Map<E, Boolean> map)

参考资料:
Collections.newSetFromMap使用场景

11.返回一个后进先出的队列视图
public static <T> Queue<T> asLifoQueue(Deque<T> deque) {
     return new AsLIFOQueue<>(deque);
 }
// 示例
Deque<String> deque = new ArrayDeque<String>();
deque.offer("b");
deque.offer("c");

Queue<String> queue = Collections.asLifoQueue(deque);
queue.offer("d");
queue.offer("a");

forList(queue); //封装foreach的方法

输出:
a 
d 
b 
c 

可以看到, 在调用方法之后添加的元素才有后进先出的特点, 参数deque的元素顺序在新的队列中没有变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值