java集合类的算法库

概述

就像C++的STL一样,java的集合类也提供了一些常用的算法,用于满足你的基本算法要求,比如排序,混排,二分查找等。

具体内容

1.排序

Collections类中的sort方法可以对实现了List接口的集合进行排序。

当集合中的对象重写了compareTo方法时,最直接的排序方法如下:

List<Employee> staff = new LinkedList<>();

. . .

Collections.sort(staff);

提醒:你还可以多加一个Comparator对象参数用于实现特定排序策略。

如果你将提供一个Comparator比较器实现特定排序策略,建议调用集合对象的sort方法。

list.sort(Comparator.comparingDouble(Employee::getSalary));

 如果你想要逆规则排序,可以调用reverseOrder()方法和reversed()方法:

list.sort(Comparator.reverseOrder());

list.sort(Comparator.comparingDouble(Employee::getSalary).reversed());

附:根据文档说明,调用sort算法,需要列表必须是可修改的,但不一定可以改变大小。这也就意味着调用subList()方法获取的视图是可以对其调用sort方法的。 

2.混排

打乱排序是一个非常常用的算法,它的使用非常简单:

Collections.shuffle(staff);

3. 二分查找

二分法查找元素是经典的算法之一,使用O(log n)的时间复杂度寻找有序列表中的元素,算法的逻辑实现细节在此就不再赘述了,调用Collections类的binarySearch()方法将会返回目标元素下标:

i = Collections.binarySearch(c, element);

i = Collections.binarySearch(c, element, comparator);

该方法如果没有找到目标元素,则会返回一个负值,其源码为: return -(low + 1);

因此,如果我们想要插入一个元素,并要求保证插入后的列表保持有序,则也要如此操作:

// 元素不存在,计算插入位置

index = Collections.binarySearch(c, element);

int insertPosition = -(index + 1);        //或者是-index-1

附:二分查找由于要根据下标访问元素,所以列表必须要可以随机访问,如果对链表使用该方法,则会退化为线性查找。

4.其他常用算法

removeIf()方法

//删除所有长度小于3的元素

words.removeIf(w -> w.length() <= 3);

reverse()方法

//逆转列表顺序

Collections.reverse(words); 

frequency()方法

//返回列表中元素o的个数

Collections.frequency(words, o);

disjoint()方法

//如果两个集合没有交集,就返回true

Collections.disjoint(words1, words2);

rotate()方法

//将列表中的元素后移d位,将末端的元素逐个补到列表头

Collections.rotate(list, d);

5.批操作

很多操作会成批的操作元素

replaceAll()方法

//用于将列表中的所有"C++"替换为"Java"

Collections.replaceAll(words, "C++", "Java");

//将所有的大写字母都转换为小写字母

words.replaceAll(String::toLowerCase);

retainAll()方法

//从coll1删除coll2中出现的所有元素

coll1.retainAll(coll2);

该方法常被用来取交集,先建立一个result集合,将其用coll1中的元素构造,然后再将default集合使用retainAll方法:

Set<Integer> default = new HashSet<>(coll1);

 default.retainAll(coll2);

removeAll()方法

//删除coll1中与coll2重复的元素

coll1.removeAll(coll2);

对此我们可以进一步,假设我们现在想要解聘员工映射Map里的部分员工,我们先可以针对员工映射里的键建立一个解聘员工id的集,然后获取员工Map的keySet(),最后对这个keySet()调用removeAll方法:

Map<String, Employee> staffMap = . . . 

Set<String> terminateIDs = . . .

terminateIDs.add(. . .); 

. . .

staffMap.keySet().removeAll(terminateIDs);

6.集合与数组的转换

将数组转换为集合很简单,可以使用java 9的小集合(of方法),也可以使用旧版本中的asList方法:

String names = . . .

List<String> staff  = List.of(names);

List<String> staff1  = List.asList(names);

将集合转换为数组则稍微复杂一点。

首先我们知道集合类是有toArray方法的,但这有一个缺陷,就是它返回的是Object[]数组,而且尽管你知道它就是String[]类型的数组,你也不能使用强制转换:

String[] names = (String[]) staff.toArray();        //Error

 事实上,我们可以对toArray方法传入一个数组构造器表达式,这样就可以转换为目标类型了:

String[] names = staff.toArray(String[]::new);

但是,在java 11以前你不能用上面这个方法,你只能用下面这两种传统的方法传入参数:

String[] names = staff.toArray(new String[0]);

String[] names = staff.toArray(new String[staff.size()]);

“  在低版本的 Java 中推荐使用初始化大小的数组,因为使用反射调用去创建一个合适大小的数组相对较慢。但是在 openJDK 6 之后的高版本中方法被优化了,传入空数组相比传入初始化大小的数组,效果是相同的甚至有时候是更优的。因为使用 concurrent 或 synchronized 集合时,如果集合进行了收缩,toArray()和size()方法可能会发生数据竞争,此时传入初始化大小的数组是危险的。  ”

7.编写自己的算法

如果编写自己的算法,应该尽可能的使用接口类,而不是具体的实现类。

例如你可能想要处理一个集合的元素,你可以接收一个ArrayList对象,但这会大大限制对这个方法的使用者,他可能想要处理的是LinkedList中的对象,用户就必须要自己进行转换。

这个时候就要问问自己,你写的这个算法关心集合的类型吗?你写的算法关心集合的顺序吗?如果你关心顺序,你应当接收List,反之,你更应该接收Collection:

public void processItems(ArrayList<Item> items) . . .

public void processItems(List<Item> items) . . .

public void processItems(Collection<Item> items) . . .

在这里,我们还可以做得更好,我们甚至可以调整为接收一个Iterable<Item>,增强for循环就是用的迭代器实现的,很多集合也都继承了Iterable接口:

 public void processItems(Iterable<Item> items) . . .

反过来,我们对于算法的返回值也要考虑到,到底是返回什么样的接口,而不是具体的集合类型,在此就不多赘述了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值