17.3 Collection的功能方法
CollectionMethods
class CollectionMethods {
public static void chapter17_3() {
Collection<String> c = new ArrayList<>();
c.add("ten");
c.add("tens");
System.out.println(c.toString());
Object[] arr = c.toArray();
System.out.println(arr.toString());
for(int i=0;i< arr.length;i++){
System.out.println(arr[i].toString());
}
String[] str = c.toArray(new String[0]);
for(int i=0;i< arr.length;i++){
System.out.println(str[i]);
}
c.clear();
System.out.println(c.toString());
}
}
17.4 可选操作
执行各种不同的添加和移除的方法在Collection接口中都是可选操作。这意味着实现类并不需要为这些方法提供功能定义。
将方法定义为可选的,是为了防止在设计类时出现接口爆炸的情况。
如果一个操作是未或支持的,那么调用接口的时候可能会导致UnsupportedOperationException异常。
注意:未获支持的操作只有在运行时才能探测到,即动态类型检查。
17.4.1 未获支持的操作
最常见的未获支持的操作,都来源于背后由固定尺寸的数据结构支持的容器。
Unsupported
class Unsupported {
static void test(String msg, List<String> list) {
Collection<String> c = list;
Collection<String> subList = list.subList(1,8); //[1,8)
Collection<String> c2 = new ArrayList<>(subList);
System.out.println("msg:" + msg);
System.out.println(c.toString());
try {
c.retainAll(c2);
System.out.println(c.toString());
}catch (Exception e) {
System.out.println("retainAll:" + e);
}
try {
c.removeAll(c2);
System.out.println(c.toString());
}catch (Exception e) {
System.out.println("removeAll:" + e);
}
try {
c.add("A");
c.add("C");
System.out.println(c.toString());
}catch (Exception e) {
System.out.println("add:" + e);
}
try {
c.remove("A");
System.out.println(c.toString());
}catch (Exception e) {
System.out.println("remove:" + e);
}
try {
list.set(0,"Z");
System.out.println(list.toString());
}catch (Exception e) {
System.out.println("list.set:" + e);
}
}
public static void chapter17_4() {
List<String> list = Arrays.asList("A B C D E F G H I J".split(" "));
test("Modifiable", new ArrayList<>(list));
test("Arrays.asList", list);
test("Unmodifiable", Collections.unmodifiableList(new ArrayList<>(list)));
}
}
Arrays.asList()返回固定尺寸的List,仅支持那些不会改变数组大小的操作;
unmodifiableList()结果在任何情况都不可修改。
17.5 List
Lists
class Lists {
static void basicTest(List<String> list) {
System.out.println(list);
list.add(1,"a");
list.add("hyh");
List<String> ls = Arrays.asList("ab cd".split(" "));
list.addAll(ls);
System.out.println(list);
String s = list.get(2);
int index = s.indexOf("hyh");
System.out.println("s:" + s + " index:" + index);
}
static void iterMotion(List<String> list) {
ListIterator<String> iter = list.listIterator();
Boolean b = iter.hasNext();
String s = iter.next();
Integer i = iter.nextIndex();
System.out.println("b:" + b + " s:" + s + " i:" + i);
}
static void iterManipulation(List<String> list) {
ListIterator<String> iter = list.listIterator();
iter.add("7");
System.out.println(list);
//删除"7"下一个元素
iter.next();
iter.remove();
System.out.println(list);
//替换"7"下一个元素
iter.next();
iter.set("xjy");
System.out.println(list);
}
public static void chapter17_5() {
List<String> list = Arrays.asList("a b c d e f".split(" "));
//修改容器大小的操作不可直接传入Arrays.asList固定容器
basicTest(new LinkedList<>(list));
iterMotion(list);
//修改容器大小的操作不可直接传入Arrays.asList固定容器
iterManipulation(new LinkedList<>(list));
}
}
基本用法
17.6 Set
HashSet:快速查找,存入HashSet的元素必须定义hashCode(),equals()
TreeSet:保持次序的Set,元素必须实现Comparable接口
LinkedHashSet:保持插入次序的Set,存入HashSet的元素必须定义hashCode(),equals()
SortedSetDemo
class SortedSetDemo {
public static void chapter17_6() {
SortedSet<String> sortedSet = new TreeSet<>();
Collections.addAll(sortedSet, "one two three four five".split(" "));
System.out.println(sortedSet);
System.out.println(sortedSet.first());
System.out.println(sortedSet.last());
String low = null,high = null;
Iterator<String> iter = sortedSet.iterator();
for(int i=0;i<sortedSet.size();i++) {
String str = iter.next();
if(i==1) {
low = str;
}
if(i==sortedSet.size()-1) {
high = str;
}
}
System.out.println("low:" + low + " high:" + high);
System.out.println(sortedSet.subSet(low,high));
System.out.println(sortedSet.headSet(high));
System.out.println(sortedSet.tailSet(low));
}
}
first():返回容器中第一个元素
last():返回容器中最后一个元素
subSet(from,to): [from,to)的子集
headSet(to):小于to的子集
tailSet(from):大于等于from的子集
17.7 Queue
QueueBehavior
interface Generator<T> {
T next();
}
class QueueBehavior {
static void test(Queue queue, Gen gen) {
for(int i=0;i<gen.size();i++) {
queue.offer(gen.next());
}
while(queue.peek()!=null) {
System.out.print(" " + queue.remove());
}
System.out.println("");
}
static class Gen implements Generator<String> {
String[] str = "one two three four five".split(" ");
int i = 0;
public int size() {
return str.length;
}
@Override
public String next() {
return str[i++];
}
}
public static void chapter17_7() {
test(new LinkedList(), new Gen());
test(new PriorityQueue(), new Gen());
}
}
LinkedList:插入顺序
PriorityQueue:自然顺序
ToDoList
class ToDoItem implements Comparable<ToDoItem> {
private char primary;
private int secondary;
private String item;
public ToDoItem(String td, char pri, int sec) {
primary = pri;
secondary = sec;
item = td;
}
@Override
public int compareTo(ToDoItem o) {
if(primary > o.primary) {
return 1;
}else if(primary == o.primary) {
if(secondary > o.secondary) {
return 1;
}else if(secondary == o.secondary) {
return 0;
}
}
return -1;
}
public String toString() {
return Character.toString(primary) + secondary + " :" + item;
}
}
class ToDoList extends PriorityQueue<ToDoItem> {
void add(String td, char pri, int sec) {
super.add(new ToDoItem(td,pri,sec));
}
public static void chapter17_7() {
ToDoList toDoList = new ToDoList();
toDoList.add("han1",'C',4);
toDoList.add("han2",'A',1);
toDoList.add("han3",'A',2);
toDoList.add("han4",'B',3);
toDoList.add("han5",'B',4);
while(!toDoList.isEmpty()){
System.out.println("" + toDoList.remove());
}
}
}
PriorityQueue:实现Comparable
17.8 Map
Maps
class Maps {
static void printKeys(Map<Integer,String> map) {
System.out.println("map.size():" + map.size());
System.out.println("map.keySet():" + map.keySet());
System.out.println("map.values():" + map.values());
}
static void test(Map<Integer,String> map) {
map.put(3,"han3");
map.put(4,"han4");
map.put(1,"han1");
map.put(2,"han2");
printKeys(map);
Integer first = map.keySet().iterator().next();
map.remove(first);
printKeys(map);
map.clear();
printKeys(map);
}
public static void chapter17_8() {
System.out.println("HashMap:");
test(new HashMap<>());
System.out.println("LinkedHashMap:");
test(new LinkedHashMap<>());
System.out.println("TreeMap:");
test(new TreeMap<>());
}
}
HashMap:基于散列表的实现
LinkedHashMap:插入次序
TreeMap:基于红黑树的实现,次序由Comparable或Comparator决定
WeakHashMap:弱键映射,允许释放映射所指向的对象
ConcurrentHashMap:线程安全Map
17.9 散列与散列码
注意:如果使用自己的类作为HashMap的键,必须同时重载hashCode()和equals()。否认使用散列的数据结构(HashSet,HashMap,LinkedHashSet或LInkedHashMap)无法正确处理你的键。
使用散列的目的:想要使用一个对象来查找另一个对象。
散列的价值在于速度,使得查询得以快速进行。
我们希望在Map中保存数量不确定的值,如果键的数量别数组的容量限制了,怎么办?
答案是:数组不保存键本身,而是通过键对象生成一个数字,将其作为数组的下标。这个数字就是散列码,由定义在Object中,可能由你的类覆盖的
hashCode()方法生成。 为解决数组容量固定的问题,不同的键可以产生相同的下标。也就是说,可能会有冲突。 如果能够保证没有冲突,那就是一个完美的散列函数。 通常,冲突由外部链接处理,数组保存值的list,然后对list里的值使用
equals() 方法进行线性查询。
如果散列函数好的话,数组的每个位置只有较少的值。因此,不是查询整个list,而是快速跳到数组的某个位置,只对很少的元素进行比较。这便是HashMap快的原因。
设计hashCode:无论何时,对同一个对象调用hashCode()都应该生成同样的值。
Joshua Bloch recipe:
(1)result = 17;
(2)计算int散列码c;
(3)合并计算散列码:
result = 37*result + c;
(4)返回result;
(5)检查hashCode(),确保相同的对象有相同的散列码;
CountedString
class CountedString {
private static List<String> created = new ArrayList<>();
private String s;
private int id = 0;
public CountedString(String str) {
s = str;
created.add(s);
for(String t:created) {
if(t.equals(s)) {
id++;
}
}
}
public String toString() {
return "String:" + s + " id:" + id + " hashCode():" + hashCode();
}
public int hashCode() {
int result = 17;
result = 37*result + s.hashCode();
result = 37*result + id;
return result;
}
public boolean equals(Object o) {
return o instanceof CountedString && s.equals(((CountedString)o).s) && id == ((CountedString)o).id;
}
public static void chapter17_9() {
Map<CountedString,Integer> map = new HashMap<>();
CountedString[] cs = new CountedString[5];
for(int i=0;i<cs.length;i++) {
cs[i] = new CountedString("ha");
map.put(cs[i],i);
}
System.out.println(map);
for(CountedString c:cs) {
System.out.println("key:" + c + " value:" + map.get(c));
}
}
}
17.11 实用方法
Utilities
class Utilities {
public static void chapter17_11() {
List<String> list = Arrays.asList("one Two three Four five".split(" "));
System.out.println("list:" + list);
System.out.println("disjoint:" + Collections.disjoint(list,Collections.singletonList("Four")));
System.out.println("max:" + Collections.max(list));
System.out.println("max_insensitive:" + Collections.max(list,String.CASE_INSENSITIVE_ORDER));
System.out.println("min:" + Collections.min(list));
System.out.println("min_insensitive:" + Collections.min(list,String.CASE_INSENSITIVE_ORDER));
List<String> sublist = Arrays.asList("Four five".split(" "));
System.out.println("indexOfSubList:" + Collections.indexOfSubList(list,sublist));
Collections.reverse(list);
System.out.println("reverse:" + list);
Collections.rotate(list,2);
System.out.println("rotate:" + list);
List<String> src = Arrays.asList("xxx yyy zzz".split(" "));
Collections.copy(list,src);
System.out.println("copy:" + list);
Collections.swap(list,0,list.size()-1);
System.out.println("swap:" + list);
Collections.shuffle(list,new Random(7));
System.out.println("shuffle:" + list);
}
}
java.util.Collections类内部的静态方法
disjoint:当两个集合没有任何相同元素时,返回true
shuffle:随机改变列表顺序
FailFast
class FailFast {
public static void chapter17_11() {
Collection<String> c = new ArrayList<>();
Iterator<String> it = c.iterator();
c.add("ha");
try {
it.next();
}catch (Exception e){
e.printStackTrace();
System.out.println("e:" + e.getMessage() + " " + e.toString());
}
}
}
容器保护机制,快速报错。在迭代遍历容器时,容器大小被(其他线程或自己)改变,会触发快速报错。
此例中,应该在添加完所有的元素后,再获取迭代器。
17.12 持有引用
java.lang.ref类库包含了一组类,这些类为垃圾回收提供了更大的灵活性。三个继承自抽象类Reference的类:SoftReference,WeakRefenrence,PhantomReference。
对象是可获得的,是指对象在程序中的某处能找到。意味着在栈中的一个引用,或有更多的中间链接。如果一个对象是"可获得的",垃圾回收器就不能回收它。 如果想继续持有对某个对象的引用,又希望垃圾回收器能自动回收它,则考虑使用Reference对象。