可选操作
简单说就是抽象类的的某些派生类实现里,或者接口的某个实现类里面,某个方法可能是无意义的,调用该方法会抛出一个异常。例如在collection的某些实现类里,里面的元素可能都是只读的,那么add这个接口是无意义的,调用会抛出UnspportedOperationException异常。
例子:
代码:
public class UnsupportedTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("A B C D E F G H I J".split(" "));
test("modifiable Copy", new ArrayList<String>(list));
test("Arrays.asList", list);
test("ummodifiable Copy", Collections.unmodifiableList(
new ArrayList<String>(list)));
}
static void test(String msg, List<String> list){
System.out.println("--- " + msg + " ---");
Collection<String> subList = list.subList(0, list.size() / 2);
//copy of the sublist
Collection<String> c = new ArrayList<String>(subList);
try {
list.retainAll(c);
} catch (Exception e) {
System.out.println("retainAll(): " + e);
}
try {
list.remove(c);
} catch (Exception e) {
System.out.println("remove(): " + e);
}
try {
list.clear();
} catch (Exception e) {
System.out.println("clear(): " + e);
}
try {
list.add("X");
} catch (Exception e) {
System.out.println("add(): " + e);
}
try {
list.addAll(c);
} catch (Exception e) {
System.out.println("addAll(): " + e);
}
try {
list.remove(list.get(0));
} catch (Exception e) {
System.out.println("remove(): " + e);
}
try {
//Collection接口没有set()方法
list.set(0, "chuqin");
} catch (Exception e) {
System.out.println("set(): " + e);
}
}
}
结果
--- modifiable Copy ---
--- Arrays.asList ---
retainAll(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
--- ummodifiable Copy ---
retainAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
clear(): java.lang.UnsupportedOperationException
add(): java.lang.UnsupportedOperationException
addAll(): java.lang.UnsupportedOperationException
remove(): java.lang.UnsupportedOperationException
set(): java.lang.UnsupportedOperationException
总结:
UnsupportedOperationException必须是一种罕见的事件。即,对于大多数类来说,所有操作都可以工作,只有在特例中才会有未获得支持的操作。在Java容器库中确实如此,因为你在99%的时间使用的容器类,如ArrayList,LinkedList,HashSet, HashMap等等,都支持所有的操作。
正确覆盖hashCode()和equals()方法
没有正确覆盖hashCode()和equals()方法,Hash类的容器不能正常工作。
例一,没有正确覆盖hashCode()和equals()方法
代码
public class Test {
public static void main(String[] args) {
Set<People> set = new HashSet<People>();
set.add(new People("chuqin"));
/*
* 意图: 为Set容器添加相同的元素,容器不会产生任何变化
* 结果:People类没有正确覆盖hashCode()和equals()方法,
* 所以new People("chuqin")产生的对象还是添加到了Set集合,这个不符合逻辑
*/
set.add(new People("chuqin"));
set.add(new People("afang"));
System.out.println(set);
}
}
class People{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public People(String name) {
super();
this.name = name;
}
public People() {
super();
}
@Override
public String toString() {
return "内存地址:" + super.toString() + ", name=" + name;
}
}
结果:
[内存地址:容器.正确覆盖equal方法.People@7852e922, name=afang, 内存地址:容器.正确覆盖equal方法.People@15db9742, name=chuqin, 内存地址:容器.正确覆盖equal方法.People@6d06d69c, name=chuqin]
例二,正确覆盖hashCode()和equals()方法
代码:
public class Test {
public static void main(String[] args) {
Set<People> set = new HashSet<People>();
set.add(new People("chuqin"));
set.add(new People("chuqin"));
set.add(new People("afang"));
/*
* People类正确覆盖hashCode()和equals()方法,
* 所以最终set集合里面只有2个对象。
*/
System.out.println(set);
}
}
class People{
String name;
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
People other = (People) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public People(String name) {
super();
this.name = name;
}
public People() {
super();
}
@Override
public String toString() {
return "内存地址:" + super.toString() + ", name=" + name;
}
}
结果:
[内存地址:容器.正确覆盖equal方法.People@586bfb4, name=afang, 内存地址:容器.正确覆盖equal方法.People@aee036c5, name=chuqin]
覆盖equals()原则
- 自反性。对于任何非null的引用值x,x.equals(x)必须返回true
- 对称性。对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
- 传递性。对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)返回true,那么x.equals(z)则必须返回true。
- 一致性。对于任何非null的引用值x、y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一直返回true,或者一致返回false。
- 对于任何非null的引用值x,x.equals(null)必须返回false
覆盖hashCode()原则
- 无论何时,对同一个对象调用hashCode()返回值一定相同。
- 不应该依赖于唯一性的对象信息。尤其是使用this(内存地址)。
- hashCode()不必返回独一无二的值,更应该注重生成速度。
- 散列码应该均匀分布,否则hash类的容器速度会变慢。
代码示例
public class Test {
private byte b;
private char c;
private int i;
private long l;
private float f;
private double d;
private String s;
private Integer integer;
private Date date;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + b;
result = prime * result + c;
long temp;
temp = Double.doubleToLongBits(d);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + ((date == null) ? 0 : date.hashCode());
result = prime * result + Float.floatToIntBits(f);
result = prime * result + i;
result = prime * result + ((integer == null) ? 0 : integer.hashCode());
result = prime * result + (int) (l ^ (l >>> 32));
result = prime * result + ((s == null) ? 0 : s.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;
Test other = (Test) obj;
if (b != other.b)
return false;
if (c != other.c)
return false;
if (Double.doubleToLongBits(d) != Double.doubleToLongBits(other.d))
return false;
if (date == null) {
if (other.date != null)
return false;
} else if (!date.equals(other.date))
return false;
if (Float.floatToIntBits(f) != Float.floatToIntBits(other.f))
return false;
if (i != other.i)
return false;
if (integer == null) {
if (other.integer != null)
return false;
} else if (!integer.equals(other.integer))
return false;
if (l != other.l)
return false;
if (s == null) {
if (other.s != null)
return false;
} else if (!s.equals(other.s))
return false;
return true;
}
}
Map
(1)HashMap
Map基于拉链法实现。元素无序的,但是查询速度最快。
(2)LinkedHashMap
类似于HashMap。但是迭代速度更快。取得“键值对”的顺序是其插入次序或者是最近最少使用(LRU)的次序。只比HashMap慢一点;而在迭代速度更快,因为它使用链表维护内部次序。
(3)TreeMap
基于红黑树的实现。查看“键”或者“键值对”时,他们会被排序(次序有Comparable或者Comparator决定)。TreeMap的特点在于,所得到的结果是进过排序的。TreeMap是以为带有subMap()方法的Map,它返回一个子树。
(4)WeakHashMap
弱键映射,允许释放映射所指向的对象;这是为了解决某类特殊问题而设计的。如果映射之外没有引用指向某个“键”,则此“键”可以被垃圾收集装置回收。
(5)ConcurrentHashMap
一种线程安全的Map,它不涉及同步加锁。
(6)IdentityHashMap
使用==代替equals()对“键”进行散列映射。
Colleactions中的静态方法
生成只读视图
调用容器的unmodifiableXXX方法。
生成同步视图
调用容器的synchronizedXXX方法。