一、Collection<E> 接口
1.1、常用方法
/*
*增删操作
*/
boolean add(E e)
boolean remove(Object o)
/*
*集合操作
*/
//对于Set接口就是求并集,对于List接口就是将两列表连接起来
boolean addAll(Collection<? extends E> c)
//对于Set接口就是求差集
boolean removeAll(Collection<?> c)
//对于Set接口就是求交集
boolean retainAll(Collection<?> c)
/*
*查找某一个或几个元素在集合中是否存在
*/
boolean contains(Object o)
boolean containsAll(Collection<?> c)
- 以上方法都会涉及集合元素的比较,这些比较都是通过调用显式参数的equals()方法来完成的,所以需要重写equals()方法。
- 以上方法的隐式参数和显式参数都只要求实现了Collection接口即可,所以可以出现隐式参数和显式参数一者为Set,一者为List的情况(但这种情况,在实现正确的equals()方法的约束下,永远返回false)。
equals()方法重写示例:
/*
*情形一:如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测
*/
class Employee{
public String name;
public int age;
public double[] bwh;
//...
@Override
public boolean equals(Object obj){ //equals()方法的入参必须为Object类型
if(this == obj) return true;
if(obj == null) return false;
if(getClass() != obj.getClass()) return false;
Employee other = (Employee)obj;
return Objects.equals(name,other.name) //引用类型比较
&& age == other.age //基本类型比较
&& Arrays.equals(bwh,other.bwh); //数组比较
}
}
/*
*情形二:如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较
*/
class Employee extends SuperClassName{
public String name;
public int age;
public double[] bwh;
//...
@Override
public boolean equals(Object obj){
if(this == obj) return true;
if(obj == null) return false;
if(!(obj instanceof SuperClassName)) return false;
SuperClassName other = (SuperClassName)obj;
//superName、superAge等是父类的属性
return Objects.equals(superName,other.superName)
&& superAge == other.superAge
&& Arrays.equals(superbwh,other.superbwh);
}
}
重写equals()方法的同时,也必须重写hashCode()方法:
class Employee{
public String name;
public int age;
public double height;
public double[] bwh;
//...
@Override
public int hashCode(){
int c = 7; //最好为质数。防止键的集聚
return c * Objects.hashCode(name) //引用类型的哈希值
+ c * age //int型的哈希值
+ c * new Double(height).hashCode() //double型的哈希值
+ c * Arrays.hashCode(bwh); //数组的哈希值
}
}
- 当equals()方法返回true时,两个对象的hashcode也必须相同;当equals()方法返回false时,两个对象的hashcode在大多数情况是不同的,在少数情况下相同。
- 集合类判断两个对象是否相等,是先判断hashcode是否相等,再判断equals()返回值是否为ture,只有两者都相等时,才认为该两个对象是相等的。所以若两个相同的对象有不同的hashcode,就会将两个相同的对象插入到Set或Map中。
- 所谓散列冲突,是指集合类在判断两个对象是不同的之后,将两者的hashcode经过运算,发现两者在桶(bucket)中的位置是相同的。两者的hashcode不相同时有可能出现散列冲突,两者的hashcode相同时一定会出现散列冲突。所以就算不同的对象有相同的hashcode,也不会影响集合类的正常运行。
1.2、迭代器方法 Iterator<E> iterator()
迭代器使用示例:
Collection<String> c = new ArrayList<String>();
Iterator<String> iter = c.iterator();
while(iter.hasNext()){
String element = iter.next();
if(...){
iter.remove();
}
}
只有对自然有序的集合使用迭代器添加元素才有实际意义。
——摘自《Core Java》
- 所以Collection接口的迭代器没有add()方法,而List接口的迭代器才有add()方法。
- 在执行 Iterator iter = c.iterator() 时,相当于生成了该集合的一个迭代器实例,若使用该迭代器实例以外的方法修改集合(包括集合自身的方法和其它迭代器实例的方法),就会抛出Concurrent ModificationException异常。
- 迭代器在add或remove时,会额外改变自己独有的游标,所以在遍历集合时进行增删操作不会出现误操作。
- 迭代器每次执行next()方法时,都会重新复制当前集合,所以迭代器可以实时反应集合的变化。
二、List<E> 接口
2.1、常用方法
//增
boolean add(E e)
void add(int index, E e)
//删
boolean remove(Object o)
E remove(int index)
//改
E set(int index, E element)
//查
E get(int index)
int indexOf(Object o)
//获取子集合
List<E> subList(int fromIndex, int toIndex)
//获取迭代器
ListIterator<E> listIterator()
ListIterator<E> listIterator(int index)
2.2、迭代器方法 ListIterator<E> listIterator()
比Iterator多出来的方法:
//Iterator只有删和查的操作,ListIterator还增加了增和改的操作
void add(E e)
void set(E e)
三、Set<E> 接口
3.1、常用方法
//增
boolean add(E e)
//删
boolean remove(Object o)
四、Map<K,V> 接口
List接口、Set接口是继承自Collection接口的,而Map接口与Collection接口是平行的关系。
4.1、常用方法
//增、改
V put(K key, V value)
void putAll(Map<? extends K,? extends V> m)
//查
V get(Object key)
boolean containsKey(Object key)
boolean containsValue(Object value)
//删
V remove(Object key)
4.2、Map的视图
三种视图:
//键值对集
Set<Map.Entry<K,V>> entrySet()
//键集
Set<K> keySet()
//值集(不是集)
Collection<V> values()
视图使用示例:
Map<String,Employee> staff = new HashMap<String,Employee>();
for(Map.Entry<String,Employee> entry : staff.entrySet()){
String key = entry.getKey(); //取键
Employee value = entry.getValue(); //取值
//...
entry.setValue(e); //重新设值
}
注意点:
entrySet()和keySet()返回的Set,既不是HashSet也不是TreeSet,而是实现了Set接口的某个其它类的对象。同时Set接口扩展了Collection接口,因此可以与使用任何集合一样使用entrySet()和keySet()返回的Set。
——摘自《Core Java》
- values()的返回类型是Collection而不是Set,是因为所谓“值集”并不是集,因为有可能重复。
五、类型转换
5.1 不同集合类之间的转换
//通过各自的构造函数
ArrayList(Collection<? extends E> c)
LinkedList(Collection<? extends E> c)
HashSet(Collection<? extends E> c)
关键点在于这些构造函数的入参都仅仅要求是Collection,So elegant!
5.2 集合类与数组之间的转换
/*
*数组转换为集合类
*/
String[] arr ={"a","b","c"};
List<String> list = Arrays.asList(arr); //此处list不可修改
//或直接 List<String> list = Arrays.asList("a","b","c");
List<String> arrList = new ArrayList<String>(list);//此处arrList可修改
/*
*集合类转换为数组
*/
List<String> arrList = new ArrayList<String>();
String[] arr = new String[arrList.size()];
arrList.toArray(arr);
六、Java库中的具体集合
——摘自《Core Java》
参考连接:
正确重写hashCode的办法
ArrayList源代码分析
Iterator的remove()和Collection的remove()
Arrays.asList()注意