集合和数组的区别:数组元素既可以是基本类型的值,也可以说对象。而集合里只能保存对象。Java集合类主要有两个接口派生而出:Collection和Map
1.Collection和Iterator接口
Collection接口是List、Set、Queue接口的父接口,具有add、remove、clear等方法来操作集合元素。
Iterator接口主要用于遍历Collection集合中的元素,Iterator对象也被称为迭代器,隐藏了各种Collection实现类的底层细节,向应用程序提供了遍历Collection集合元素的统一编程接口,接口里定义了如下4个方法:
- boolean hasNext():如果被迭代的集合元素还没有被遍历完,则返回true
- Object next():返回集合里的下一个元素
- void remove():删除集合里上一次next方法返回的元素
- void forEachRemaining(Consumer action):可使用Lambda表达式来遍历集合元素
Iterator必须依附于Collection对象。用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只有通过Iterator的remove()方法删除上一次next方法返回的元素才可以,否则将会引发java.util.ConcurrentModificationException异常
使用Predicate简化集合的运算
例如:以下程序统计书名中出现“疯狂”字符串的图书数量,其中Predicate是一个函数式接口:
public class PredicateTest2
{
public static void main(String[] args)
{
//创建集合(略)
...
System.out.println(calAll(books,ele->((String)ele).contains("疯狂")));
}
public static int calAll(Collection books,Predicate p)
{
int total = 0;
for(Object obj : books)
{
//使用Predicate的test()方法判断该对象是否满足Predicate指定的条件
if(p.test(obj))
{
total++;
}
}
return total;
}
}
Stream流式API
Stream提供商大量的方法进行聚集操作,对于大部分聚集方法而言,每个Stream只能执行一次
- 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法
- 末端方法:是对流的最终操作,当对某个Stream执行末端方法后,该流将会被“消耗”且不再可用
常用的中间方法:
- filter(Predicate predicate):过滤Stream中所用不符合predicate的元素
- mapToXxx(ToXxxFunction mapper):使用ToXxxFunction对流中的元素执行一对一的转换,该方法返回的新流中包含了ToXxxFunction转换生成的所有元素
- peek(Consumer action):依次对每一个元素执行一些操作,该方法返回的流与原有流包含相同的元素,该方法主要用于调试
- distinct():该方法用于排序流中所有重复元素(判断标准是使用equals()比较返回true),这是一个有状态的方法
- sorted():该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法
- limit(long maxSize):该方法用于保证对流的后续访问中最大允许访问的元素个数。这是一个有状态的、短路方法
常用的末端方法:
- forEach(Consumer action):遍历流中所有元素,对每个元素执行action
- toArray():将流中所有元素转换为一个数组
- reduce():有三个重载版本,都用于通过某种操作来合并流中的元素
- anyMatch(Predicate predicate):判断流中是否至少包含一个元素符合Predicate条件
- allMatch(Predicate predicate):判断流中是否每个元素都符合Predicate条件
- noneMatch(Predicate predicate):判断流中是否所有元素都不符合Predicate条件
- findFirst():返回流中的第一个元素
- findAny():返回流中任意一个元素
通过使用Collection接口提供的stream()默认方法简化代码:
public class CollectionStream
{
public static void main(String[] args)
{
System.out.println(books.stream().filter(ele->((String)ele).contains("疯狂")).count());
books.stream().mapToInt(ele->((String)ele.length()).forEach(System.out::println);
}
}
2.Set集合
HasnSet类
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定该对象在HashSet中的存储位置。如果有两个元素通过equals()方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同的位置,依然可以添加成功。所以当把一个对象放入HashSet时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode()方法,规则是如果两个对象通过equals()方法返回true,这两个对象的hashCode值也应该相同
补充:hash算法的功能
它能保证快速查找被检索的对象,hash算法的价值在于速度。当需要查询集合中某个元素时,hash算法可以直接根据该元素的hashCode值计算出该元素的存储位置,从而快速定位该元素。
当程序把可变对象添加到HashSet中之后,尽量不要去修改该集合元素中参与计算的hashCode()、equals()的实例变量,否则将会导致HashSet无法正确操作这些集合元素
LinkedHashSet类
HashSet的子类,使用链表维护元素的次序,是的元素看起来是以茶入的顺序保存的,遍历LinkedHashSet集合里的元素时,将会按元素的添加顺序来访问集合里的元素
TreeSet类
是SortedSet接口的实现类,可以确保集合元素处于排序状态,与HashSet集合相比,TreeSet还提供了如下几个额外的方法:
- Comparator comparator():如果TreeSet采用了定制排序,则该方法返回定制排序所使用的Comparator,如果TreeSet采用了自然排序则返回null
- Object first():返回第一个元素
- Object last():返回最后一个元素
- Object lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要时TreeSet集合里的元素),相反使用Object higher(Object e)
- SortedSet subSet(Object fromElement,Object toElement):返回此Set的子集合,范围从fromElement(包含)到toElement(不包含)
- SortedSet headSet(Object toElement):返回此Set的子集,由小于toElement的元素组成
- SortedSet tailSet(Object fromElement):返回此Set的子集,由大于或等于fromElement的元素组成
如果试图把一个对象添加到TreeSet时,该对象的类必须实现Comparable接口,否则程序将会抛出异常。已经实现了Comparable接口的常用类有:
- BigDecimal、BigInteger以及所以的数值类型对应的包装类:按它们对应的数值大小进行比较
- Character:按字符的UNICODE值进行比较
- Boolean:true对应的包装类实例大于false对应的包装类实例
- String:按字符串中字符的UNICODE值进行比较
- Date、Time:后面的时间、日期比前面的大
如果希望TreeSet能正常运作,只能添加同一种类型的对象
3.List集合
List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。与Set集合相比,List增加了根据索引来插入、替换和删除集合元素的方法。除此之外还增加了两个默认方法:
- void replaceAll(UnaryOperator operator):根据operator指定的计算规则重新设置List集合的所有元素
- void sort(Comparator c):根据Comparator参数对List集合的元素排序
关于remove(Object o):查阅源码发现List将会调用o所在类的equals()方法依次与集合元素进行比较,该equals()方法以某个集合元素作为参数时返回true,List将会删除该元素。
List还额外提供了一个listIterator()方法,返回一个ListIterator对象,ListIterator接口继承了Iterator接口,提供了专门操作List的方法。
- boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素
- Object previous():返回该迭代器的上一个元素
- void add(Object o):在指定位置插入一个元素
所以ListIterator增加了向前迭代的功能,但一开始也需要采用正向迭代
ArrayList和Vector实现类
ArrayList和Vector在用法上几乎完全相同,其显著区别是:ArrayList是县城不安全的,当多个线程访问同一个ArrayList集合时如果有超过一个线程修改了ArrayList集合,则程序必须手动保证该集合的同步性。但Vector集合是线程安全的,锁业性能比ArrayList性能要低。但是还是不推荐使用Vector实现类。
固定长度的List
工具类Arrays提供了asList(Object...a)方法,该方法可以把一个数组或指定个数的对象转换成一个List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例,程序只能遍历访问该集合里的元素,不可增加、删除该集合里的元素。,否则会引发UnsupportedOperationException异常
4.Queue集合
- void add(Object e):将指定元素加入此队列的尾部
- Object element():获取队列头部的元素,但不删除该元素
- boolean offer(Object e):将指定元素加入此队列的尾部。当使用有容量限制的队列时,此方法通常比add(Object e)方法更好
- Object peek():获取队列头部的元素,但是不删除该元素。如果此队列为空,则返回null
- Object poll():获取队列的头部元素并删除该元素,如果此队列为空,则返回null
- Object remove():获取队列头部的元素,并删除该元素
PriorityQueue实现类
保存队列元素的顺序不是按加入队列的顺序,而是按队列元素的大小进行重新排序。因此调用peek()或者poll()取出的是队列中最小的元素。
Deque接口与ArrayDeque实现类
Deque是Queue接口的子接口,代表一个双端队列。
Deque不仅可以当成双端队列使用,还可以被当成栈来使用,因为该类里还包含了pop、push两个方法
Deque接口提供了一个典型的实现类ArrayDeque。创建Deque时可以指定一个numElements参数,如果不指定,Deque底层数组的长度为16。
LinkedList实现类
它是一个List集合,可以根据索引来随机访问集合中的元素。除此之外LinkedList还实现了Deque接口,可以当成双端队列来使用,因此既可以被当成“栈”来使用,也可以当成队列使用。内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能较差,但在插入、删除元素时性能比较出色。但总的来说,ArrayList的性能比LinkedList的性能要好,因此大部分时候都考虑使用ArrayList。
5.Map集合
Map用于保存具有映射关系的数据,key和value之间存在单向一对一关系。从Java的源码来看,Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合。Map有时也被称为字典或关联数组。
常用方法:
- void clear():删除该Map对象中所有key-value对
- Set entrySet():返回Map中包含的key-value对所组成的Set集合,每个集合元素都是Map.Entry对象
- Object get(Object key):返回指定key所对应的value;如果此Map中不包含该key,则返回null
- boolean isEmpty():查询该Map是否为空
- Set keySet():返回该Map中所有key组成的Set集合
- Object put(Object key,Object value):添加一个key-value对,如果当前Map中已有一个与该key相等的key-value对,则新的key-value对会覆盖原来的key-value对
- void putAll(Map m):将指定Map中的key-value对复制到本Map中
- Object remove(Object key):删除指定key所对应的key-value对,返回被删除key所关联的value,如果该key不存在,则返回null
- boolean remove(Object key,Object value):删除指定key、value所对应的key-value对。如果成功删除返回true
- int size():返回key-value对的个数
- Collection values():返回该Map里所有value组成的Collection
Map中包括一个内部类Entry,该类封装了一个key-value对包含如下三个方法:
- Object getKey():返回该Entry里包含的key值
- Object getValue():返回该Entry里包含的value值
- Object setValue(V value):设置该Entry里包含的value值,并返回新设置的value值
HashMap和Hashtable实现类
它们之间的关系完全类似于ArrayList和Vector的关系:Hashtable是一个古老的Map实现类。典型区别:
- Hashtable是一个线程安全的Map实现,但HastMap线程不安全,所以HashMap比Hashtable的性能高一点。但如果有多个线程访问同一个Map对象时,Hashtable实现类会更好
- Hashtable不允许使用null作为key和value,而HashMap可以
由于HashMap里的key不能重复,所以HashMap里最多只有一个key-value对的key为null,但可以有无数多个key-value对的value为null。除此之外,与HashSet集合不能保证元素的顺序一样,HashMap、Hashtable也不能保证其中key-value对的顺序。
LinkedHashMap实现类
LindedHashMap也使用双项链表来维护key-value对的次序,迭代顺序与key-value对的插入顺序保持一致
使用Properties读写属性文件
Properties类是Hashtable类的子类,夜不保证key-value对之间的次序,key、value都是字符串类型。
String getProperty(String key):获取Properties中指定属性名对应的属性值,类似于Map的get(Object key)方法
String getProperty(String key,String defaultValue):如果Properties中不存在指定的key时,则该方法指定默认值
Object setProperty(String key,String value):设置属性值,类似于Hashtable的put()方法
void load(InputStream inStream):从属性文件中加载key-value对,把加载到的key-value对追加到Properties里
void store(OutputStream out,String comments):将Properties中的key-value对输出到指定的属性文件中
6.操作集合的工具类:Collections
排序操作
- void reverse(List list):反转指定List集合中元素的顺序
- void shuffle(List list):对List集合元素进行随机排序(模拟“洗牌”)
- void sort(List list):按升序进行排序
- void sort(List list,Comparator c):根据指定Comparator产生的顺序对List集合元素进行排序
- void swap(List list,int i, int j):交换元素
- void rotate(List list, int distance):当distance为正数时,将list集合的后distance个元素“整体”移到前面;当distance为负数时,将list集合的前distance个元素“整体”移到后面,该方法不会改变集合的长度
查找、替换操作(略)
同步控制
Collections类中提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
设置不可变集合
emptyXxx():返回一个空的、不可变的集合对象,此处的集合既可以说List,也可以是SortedSet、Set,还可以是Map、SortedMap等
singletonXxx():返回一个只包含指定对象的、不可变的集合对象,此处的集合既可以是List,还可以是Map
unmodifiableXxx():返回指定集合对象的不可变视图,此处的集合既可以是List,也可以是Set、SortedSet,还可以是Map、SorteMap等