想要熟练写出算法题,必须要做到对 java 中的集合做到熟练使用。java 中的集合实现了各种数据结构,我们可以直接使用这些数据结构,更加专注与解决算法问题而不是实现这些数据结构。
集合主要分为两大类,包括 Collection 和 Map,Collection 表示一组对象,Map 表示一组映射关系或键值对。
下图是 Collection 之间的继承和实现关系:
下图是 Map 之间的继承和实现关系:
1.Collection 常用功能
Collection是所有单列集合的父接口,因此在 Collection 中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:
1、添加元素
(1)add(E obj):添加元素对象到当前集合中。
(2)addAll(Collection<? extends E> other):添加other集合中的所有元素对象到当前集合中,即this = this ∪ other。
2、删除元素
(1)boolean remove(Object obj) :从当前集合中删除第一个找到的与obj对象equals返回true的元素。
(2)boolean removeAll(Collection<?> coll):从当前集合中删除所有与coll集合中相同的元素。即this = this - this ∩ coll
3、判断元素
(1)boolean isEmpty():判断当前集合是否为空集合。
(2)boolean contains(Object obj):判断当前集合中是否存在一个与obj对象equals返回true的元素。
(3)boolean containsAll(Collection<?> c):判断c集合中的元素是否在当前集合中都存在。即c集合是否是当前集合的“子集”。
4、查询
(1)int size():获取当前集合中实际存储的元素个数。
(2)Object[] toArray():返回包含当前集合中所有元素的数组。
5、交集
(1)boolean retainAll(Collection<?> coll):当前集合仅保留与c集合中的元素相同的元素,即当前集合中仅保留两个集合的交集,即this = this ∩ coll。
2.Iterator 迭代器
Iterator 接口主要用于遍历元素,的常用方法如下:
public E next()
:返回迭代的下一个元素。public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。
public class IteratorDemo {
public static void main(String[] args) {
// 使用多态方式 创建对象
Collection<String> coll = new ArrayList<String>();
// 添加元素到集合
coll.add("张三");
coll.add("李四");
coll.add("王五");
Iterator<String> it = coll.iterator();
// 泛型指的是 迭代出 元素的数据类型
while(it.hasNext()){ //判断是否有迭代元素
String s = it.next();//获取迭代出的元素
System.out.println(s);
}
}
}
注意:不要在使用Iterator迭代器进行迭代时,调用 Collection 的 remove(xx) 方法,否则会报异常java.util.ConcurrentModificationException,或出现不确定行为。可以使用迭代器的 remove() 方法。
3.List 集合
List 继承至 Collection 接口,继承了 Collection 接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
1、添加元素
- void add(int index, E ele)
- boolean addAll(int index, Collection<? extends E> eles)
2、获取元素
- E get(int index)
- List subList(int fromIndex, int toIndex)
3、获取元素索引
- int indexOf(Object obj)
- int lastIndexOf(Object obj)
4、删除和替换元素
- E remove(int index)
- E set(int index, E ele)
List集合特有的方法都是跟索引相关。
3.1 ArrayList 集合
实现了 List 接口。可以使用 Collection 和 List 中提供的方法。
3.2 Vector 集合
版本古老,不支持快速失败,很少使用。
3.3 LinkedList 集合
链表结构。方便元素添加、删除的集合。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。
JDK1.6 之后 LinkedList 实现了 Deque 接口。双端队列也可用作 LIFO(后进先出)堆栈。如果要使用堆栈结构的集合,可以考虑使用LinkedList,而不是Stack。
堆栈方法 | 等效Deque方法 |
---|---|
push(e) | addFirst(e) |
pop() | removeFirst() |
peek() | peekFirst() |
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
//入栈
list.addFirst(1);
list.addFirst(2);
list.addFirst(3);
//出栈: LIFO(后进先出)
System.out.println(list.removeFirst());//3
System.out.println(list.removeFirst());//2
System.out.println(list.removeFirst());//1
//栈空了,会报异常java.util.NoSuchElementException
System.out.println(list.removeFirst());
}
用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。
Queue 方法 | 等效 Deque 方法 |
---|---|
add(e) | addLast(e) |
offer(e) | offerLast(e) |
remove() | removeFirst() |
poll() | pollFirst() |
element() | getFirst() |
peek() | peekFirst() |
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
//入队
list.addLast(1);
list.addLast(2);
list.addLast(3);
//出队, FIFO(先进先出)
System.out.println(list.pollFirst());//1
System.out.println(list.pollFirst());//2
System.out.println(list.pollFirst());//3
//队空了,返回null
System.out.println(list.pollFirst());//null
}
每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。
3.4 ListIterator
List 集合额外提供了一个 listIterator() 方法,该方法返回一个 ListIterator 对象, ListIterator 接口继承了 Iterator 接口,提供了专门操作 List 的方法:
- void add():通过迭代器添加元素到对应集合
- void set(Object obj):通过迭代器替换正迭代的元素
- void remove():通过迭代器删除刚迭代的元素
- boolean hasPrevious():如果以逆向遍历列表,往前是否还有元素。
- Object previous():返回列表中的前一个元素。
- int previousIndex():返回列表中的前一个元素的索引
- boolean hasNext()
- Object next()
- int nextIndex()
public static void main(String[] args) {
List<String> c = new ArrayList<>();
c.add("张三");
c.add("李四");
c.add("王五");
//从指定位置往前遍历
ListIterator<String> listIterator = c.listIterator(c.size());
while(listIterator.hasPrevious()){
String previous = listIterator.previous();
System.out.println(previous);
}
}
4.Set 集合
Set 接口是 Collection 的子接口,set 接口没有提供额外的方法。但是比Collection
接口更加严格了,Set 集合不允许包含相同的元素。Set集合支持的遍历方式和Collection集合一样:foreach和Iterator。Set的常用实现类有:HashSet、TreeSet、LinkedHashSet。
4.1 HashSet
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。java.util.HashSet
底层的实现其实是一个java.util.HashMap
支持,然后 HashMap 的底层物理实现是一个 Hash 表。
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。因此,存储到HashSet的元素要重写hashCode和equals方法。例如实现一个 Employee 类,要求 name 和 birthday 一样的视为同一个员工:
public class Employee {
private String name;
private MyDate birthday;
// 这里 构造方法、getter、setter 都省略
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((birthday == null) ? 0 : birthday.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (birthday == null) {
if (other.birthday != null)
return false;
} else if (!birthday.equals(other.birthday))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
// toString() 这里省略
}
public class MyDate {
private int year;
private int month;
private int day;
// 这里 构造方法、getter、setter 都省略
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + day;
result = prime * result + month;
result = prime * result + year;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyDate other = (MyDate) obj;
if (day != other.day)
return false;
if (month != other.month)
return false;
if (year != other.year)
return false;
return true;
}
// toString() 这里省略
}
4.2 LinkedHashSet
LinkedHashSet 是 HashSet 的子类,它在HashSet的基础上,在结点中增加两个属性 before 和 after 维护了结点的前后添加顺序。java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构。LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
4.3 TreeSet
底层结构:里面维护了一个TreeMap,基于红黑树实现。
特点:
- 不允许重复
- 实现排序:自然排序或定制排序
如何实现去重的?
- 如果使用的是自然排序,则通过调用实现的compareTo方法
- 如果使用的是定制排序,则通过调用比较器的compare方法
如何排序?
-
自然排序:让待添加的元素类型实现Comparable接口,并重写compareTo方法
-
定制排序:创建Set对象时,指定Comparator比较器接口,并实现compare方法
下面给出定制排序的例子:
public class Student{
private int id;
private String name;
// 这里省略了构造方法、getter、setter、toString
}
public void test3(){
TreeSet<Student> set = new TreeSet(new Comparator<Student>(){
@Override
public int compare(Student o1, Student o2) {
return o1.getId() - o2.getId();
}
});
set.add(new Student(3,"张三"));
set.add(new Student(1,"李四"));
set.add(new Student(2,"王五"));
set.add(new Student(3,"张三凤"));
System.out.println("元素个数:" + set.size());
for (Student stu : set) {
System.out.println(stu);
}
}
5.Map 集合
Map<K,V>
接口下的集合与Collection<E>
接口下的集合,它们存储数据的形式不同。
Collection
中的集合,元素是孤立存在的,向集合中存储元素采用一个个元素的方式存储。Map
中的集合,元素是成对存在的。每个元素由键与值两部分组成,通过键可以找对所对应的值。Collection
中的集合称为单列集合,Map
中的集合称为双列集合。Map
中的集合不能包含重复的键,值可以重复。
Map 的常用方法如下:
1、添加操作
- V put(K key,V value)
- void putAll(Map<? extends K,? extends V> m)
2、删除
- void clear()
- V remove(Object key)
3、元素查询的操作
- V get(Object key)
- boolean containsKey(Object key)
- boolean containsValue(Object value)
- boolean isEmpty()
4、元视图操作的方法:
- Set keySet()
- Collection values()
- Set<Map.Entry<K,V>> entrySet()
5、其他方法
- int size()
Map 的遍历可以采用如下几种方式:
// map.keySet()
System.out.println("所有的key:");
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key);
}
// map.values()
System.out.println("所有的value:");
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
// map.entrySet()
System.out.println("所有的映射关系");
Set<Map.Entry<String,String>> entrySet = map.entrySet();
for (Map.Entry<String,String> entry : entrySet) {
System.out.println(entry.getKey()+"->"+entry.getValue());
}
5.1 HashMap 和 HashTable
Map 接口提供的方法都可以使用。
5.2 LinkedHashMap
LinkedHashMap 是 HashMap 的子类。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。
5.3 TreeSet
基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
TreeMap<String,Integer> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
5.4 Properties
Properties 类是 Hashtable 的子类,Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法。
代码示例:
public static void main(String[] args) {
Properties properties = System.getProperties();
String p2 = properties.getProperty("file.encoding");//当前源文件字符编码
System.out.println(p2);
}
6.Collections 工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类。Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法:
- public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)在coll集合中找出最大的元素,集合中的对象必须是T或T的子类对象,而且支持自然排序
- public static T max(Collection<? extends T> coll,Comparator<? super T> comp)在coll集合中找出最大的元素,集合中的对象必须是T或T的子类对象,按照比较器comp找出最大者
- public static void reverse(List<?> list)反转指定列表List中元素的顺序。
- public static void shuffle(List<?> list) List 集合元素进行随机排序,类似洗牌
- public static <T extends Comparable<? super T>> void sort(List list)根据元素的自然顺序对指定 List 集合元素按升序排序
- public static void sort(List list,Comparator<? super T> c)根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
- public static void swap(List<?> list,int i,int j)将指定 list 集合中的 i 处元素和 j 处元素进行交换
- public static int frequency(Collection<?> c,Object o)返回指定集合中指定元素的出现次数
- public static void copy(List<? super T> dest,List<? extends T> src)将src中的内容复制到dest中
- public static boolean replaceAll(List list,T oldVal,T newVal):使用新值替换 List 对象的所有旧值
欢迎关注公众号。