thinking-in-java(11) 持有对象

 【11.1】泛型和类型安全的容器

(1)ArrayList<Apple> 中尖括号括起来的是: 类型参数,它指定了这个容器实例可以保存的类型;


【荔枝:有泛型和没有泛型的区别】

class Apple {
	private static long counter;
	private final long id = counter++;

	public long id() {
		return id;
	}
}
class Orange {
}
// 没有泛型的 Apples 和 Oranges 类
public class ApplesAndOrangesWithoutGenerics {
	 @SuppressWarnings("unchecked") // 禁止"不受检查的异常"的警告信息
	public static void main(String[] args) {
		ArrayList apples = new ArrayList();
		for (int i = 0; i < 3; i++)
			apples.add(new Apple());
		apples.add(new Orange());
		for (int i = 0; i < apples.size(); i++)
			((Apple) apples.get(i)).id(); // 抛出运行时异常;不能把 Orange 强转为 Apple的。
	}
} 
//有泛型的 Apples 和 Oranges 类
public class ApplesAndOrangesWithGenerics {
	public static void main(String[] args) {
		ArrayList<Apple> apples = new ArrayList<Apple>();
		for (int i = 0; i < 3; i++)
			apples.add(new Apple());
		// apples.add(new Orange());
		for (int i = 0; i < apples.size(); i++)
			System.out.println(apples.get(i).id());
		// Using foreach:
		for (Apple c : apples)
			System.out.println(c.id());
	}
}

2)编译器阻止将 Orange 放置到 apples容器中, 它是一个编译器错误,而不是运行时错误;


 【(向上转型)泛型参数为 基类,可以添加子类对象。基类指针指向子类对象。】

class GrannySmith extends Apple {}
class Gala extends Apple {}
class Fuji extends Apple {}
class Braeburn extends Apple {}

public class GenericsAndUpcasting {
	public static void main(String[] args) {
		// (向上转型)泛型参数为 基类,可以添加子类对象。基类指针指向子类对象。
		ArrayList<Apple> apples = new ArrayList<Apple>();
		apples.add(new GrannySmith()); // GrannySmith 是 Apple的子类
		apples.add(new Gala()); // Gala 是 Apple的子类
		apples.add(new Fuji()); // Fuji 是 Apple的子类
		apples.add(new Braeburn()); // Braeburn 是 Apple的子类
		for (Apple c : apples)
			System.out.println(c);
	}
} 

// 打印结果
chapter11.GrannySmith@2a139a55 // @ 后面的字符串是 对象散列码的无符号16进制字符串;(Object.hashCode() 函数)
chapter11.Gala@15db9742
chapter11.Fuji@6d06d69c
chapter11.Braeburn@7852e922
【11.2】基本概念

1)java容器类类库的用途是 保存对象,有两种类型的容器;

容器1)Collection:一条记录保存一个值;

List:必须按照插入的顺序保存元素;(ArrayList  LinkedList) 不管重复与否;

ArrayList:底层实现是数组,查询速度快;

LinkedList: 底层实现是链表,删除速度快;


Set:不能有重复元素且无序;

HashSet:通过哈希函数获取函数,是最快的获取元素的方式;元素的存储顺序看起来并无实际意义(或不关心元素的存储顺序);无序不重复HashSet,底层用HashMap.key 实现;

TreeSet:如果存储顺序重要;它按照比较结果升序保存对象;无序不重复TreeSet,底层用 TreeMap.key 实现;

LinkedHashSet:它按照元素被添加的顺序保存对象; 底层用 LinkedHashMap.key 实现;


Queue: 只允许在容器的一端插入对象,并从另外一端移除对象;


容器2)Map:一条记录保存两个值,key值和value值,即键值对;

HashMap:没有按照任何明显的顺序保存元素,且通过哈希查找算法保证了查找的快速;

TreeMap:按照比较结果的升序保存键;

LinkedHashMap:按照元素的插入顺序保存键值对,同时还保留了HashMap的查询速度;底层用  HashMap 实现;



2)Collection接口概括了序列的概念。一种存放一组对象的方式;(Collection 就是是一个对象数组);

// 简单的容器操作实例
public class SimpleCollection {
	public static void main(String[] args) {
		//ArrayList间接实现了 Collection 接口
		Collection<Integer> c = new ArrayList<Integer>();
		for (int i = 0; i < 10; i++)
			c.add(i); // 自动装箱
		for (Integer i : c)
			System.out.print(i + ", ");
		
		System.out.println("\n======");
		//HashSet间接实现了 Collection 接口
		c = new HashSet<Integer>();
		for (int i = 0; i < 10; i++)
			c.add(i); // 自动装箱
		for (Integer i : c)
			System.out.print(i + ", ");
	}
}  
// 类继承结构如下: 
// ArrayList 的类结构层次
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
// AbstractList
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
// List
public interface List<E> extends Collection<E> 

// HashSet 的类结构层次
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
// AbstractSet
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E>
// Set
public interface Set<E> extends Collection<E> 
// AbstractCollection
public abstract class AbstractCollection<E> implements Collection<E> 
// 打印结果: 
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
======
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
3)所有 Collection 都可以用到 foreach 语法;


【11.3】添加一组元素

// 添加一组元素荔枝
public class AddingGroups {
	public static void main(String[] args) {
		// Arrays.asList() 接受一个数组或用 逗号分隔的元素列表 并转换为一个 List对象.
		Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(
				1, 2, 3, 4, 5));
		Integer[] moreInts = { 6, 7, 8, 9, 10 };
		collection.addAll(Arrays.asList(moreInts));
		
		// Collections.addAll() 方法 达到同样的效果.
		Collections.addAll(collection, 11, 12, 13, 14, 15);
		Collections.addAll(collection, moreInts);
		for(Integer i : collection) {
			System.out.print(i + ", ");
		}
		/* Arrays.asList()方法返回的是 ArrayList */
		List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);
		list.set(1, 99); // OK -- modify an element
		list.add(21); // 报错,因为潜在数组不会改变原有大小.
	}
} 
// 打印结果: 抛出异常
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10, Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)
	at chapter11.AddingGroups.main(AddingGroups.java:27)
// Arrays.asList() 方法详情
 @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }
// Arrays$ArrayList 内部类 
/**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

        ArrayList(E[] array) {
            a = Objects.requireNonNull(array);
        }

        @Override
        public int size() {
            return a.length;
        }

        @Override
        public Object[] toArray() {
            return a.clone();
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                return Arrays.copyOf(this.a, size,
                                     (Class<? extends T[]>) a.getClass());
            System.arraycopy(this.a, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }

        @Override
        public E get(int index) {
            return a[index];
        }

        @Override
        public E set(int index, E element) {
            E oldValue = a[index];
            a[index] = element;
            return oldValue;
        }

        @Override
        public int indexOf(Object o) {
            E[] a = this.a;
            if (o == null) {
                for (int i = 0; i < a.length; i++)
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) != -1;
        }

        @Override
        public Spliterator<E> spliterator() {
            return Spliterators.spliterator(a, Spliterator.ORDERED);
        }

        @Override
        public void forEach(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            for (E e : a) {
                action.accept(e);
            }
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) {
            Objects.requireNonNull(operator);
            E[] a = this.a;
            for (int i = 0; i < a.length; i++) {
                a[i] = operator.apply(a[i]);
            }
        }

        @Override
        public void sort(Comparator<? super E> c) {
            Arrays.sort(a, c);
        }
    }
【代码解说】

(1)注意区分Collection的成员方法,即addAll() 方法 和 Collections.addAll() 类方法;(干货——Collections.addAll() 类方法是添加元素的首选方式

(2)Collection.addAll() 成员方法只能接受另一个Collection对象作为参数,没有 Arrays.asList() 和 Collections.addAll() 类方法灵活,后面的两个方法都可以使用 可变参数列表

(3)可以直接把 Arrays.asList() 的输出作为输入, 但其输出的数组是固定的,不能调整尺寸了;参见 Arrays.asList() 方法的内部调用详情,它实际上实例化了 Arrays的内部类ArrayList 的一个实例并返回的,且ArrayList是基于数组的,该数组的大小是固定的;


【看个荔枝】

class Snow {}
class Powder extends Snow {}
class Light extends Powder {}
class Heavy extends Powder {}
class Crusty extends Snow {}
class Slush extends Snow {}

public class AsListInference {
	public static void main(String[] args) {
		/* Arrays.asList() 方法 产生大小固定的Arrays$ArrayList内部类实例 */
		List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder()); 
		System.out.println("===snow1===");
		for (int i = 0; i < snow1.size(); i++) {
			System.out.println(snow1.get(i));
		}
		/* Arrays.asList() 方法 产生大小固定的Arrays$ArrayList内部类实例 */
		 List<Snow> snow2 = Arrays.asList(new Light(), new Heavy());  
		 System.out.println("===snow2===");
		 for (int i = 0; i < snow2.size(); i++) {
				System.out.println(snow2.get(i));
		 }
		 System.out.println(snow2.getClass().getSimpleName().toLowerCase()); // 输出的是 arraylist
		 
		List<Snow> snow3 = new ArrayList<>();
		/* 推荐用 Collections.addAll() 向 Collection实例中添加元素.( Collections.addAll() 是添加元素的首选方法) */
		Collections.addAll(snow3, new Light(), new Heavy()); // 向snow3容器添加 后面两个元素;(可变参数列表)
		System.out.println("===snow3===");
		for (int i = 0; i < snow3.size(); i++) {
			System.out.println(snow3.get(i));
		}
		/* (插入了一条线索<Snow>) Arrays.asList() 方法  */
		/* 告诉 */
		List<Snow> snow4 = Arrays.<Snow> asList(new Light(), new Heavy()); 
		System.out.println("===snow4===");
		for (int i = 0; i < snow4.size(); i++) {
			System.out.println(snow4.get(i));
		}
	}
}
// 打印结果:
===snow1===
chapter11.Crusty@2a139a55
chapter11.Slush@15db9742
chapter11.Powder@6d06d69c
===snow2===
chapter11.Light@7852e922
chapter11.Heavy@4e25154f
arraylist
snow2 = [chapter11.Light@7852e922, chapter11.Heavy@4e25154f]
===snow3===
chapter11.Light@70dea4e
chapter11.Heavy@5c647e05
===snow4===
chapter11.Light@33909752
chapter11.Heavy@55f96302

【11.4】容器的打印

// 常用容器打印荔枝
public class PrintingContainers {
	static Collection fill(Collection<String> collection) {
		collection.add("rat1");
		collection.add("cat2");
		collection.add("dog3");
		collection.add("dog3");
		return collection;
	}
	static Map fill(Map<String, String> map) {
		map.put("rat1", "Fuzzy");
		map.put("cat2", "Rags2");
		map.put("dog3", "Bosco3");
		map.put("dog3", "Spot3");
		return map;
	}
	public static void main(String[] args) {
		System.out.println("ArrayList = " + fill(new ArrayList<String>()));
		System.out.println("LinkedList = " + fill(new LinkedList<String>()));
		System.out.println("HashSet = " + fill(new HashSet<String>())); // 无序不重复HashSet,底层用HashMap.key 实现
		System.out.println("TreeSet = " + fill(new TreeSet<String>())); // 无序不重复TreeSet,底层用 TreeMap.key 实现;
		System.out.println("LinkedHashSet = " + fill(new LinkedHashSet<String>())); // 底层用 LinkedHashMap.key 实现;
		System.out.println("HashMap = " + fill(new HashMap<String, String>()));
		System.out.println("TreeMap = " + fill(new TreeMap<String, String>()));
		System.out.println("LinkedHashMap = " + fill(new LinkedHashMap<String, String>())); // 底层用 HashMap 实现
	}
} 
// 常用容器的打印荔枝: 
ArrayList = [rat1, cat2, dog3, dog3]
LinkedList = [rat1, cat2, dog3, dog3]
HashSet = [cat2, dog3, rat1]
TreeSet = [cat2, dog3, rat1]
LinkedHashSet = [rat1, cat2, dog3]
HashMap = {cat2=Rags2, dog3=Spot3, rat1=Fuzzy}
TreeMap = {cat2=Rags2, dog3=Spot3, rat1=Fuzzy}
LinkedHashMap = {rat1=Fuzzy, cat2=Rags2, dog3=Spot3}

【11.5】 List

1)ArrayList: 擅长于随机访问元素,但在List的中间插入和删除元素时速度较慢;

2)LinkedList:擅长于 插入和删除元素,但随机访问元素时速度较慢;

【荔枝-List方法列表】

// List 方法的荔枝
public class ListFeatures {
	public static void main(String[] args) {
		Random rand = new Random(47);
		List<Pet> pets = Pets.arrayList(7);
		print("1: " + pets);
		
		Hamster h = new Hamster();
		pets.add(h); // 添加元素,自动调整大小
		print("2: " + pets);
		
		print("3: " + pets.contains(h)); // 包含某个元素
		
		pets.remove(h); // 移除某个元素.
		Pet p = pets.get(2);
		print("4: " + p + " " + pets.indexOf(p)); // 元素的序号.
		
		Pet cymric = new Cymric();
		print("5: " + pets.indexOf(cymric)); // 查找元素的序号,没有的话,返回-1
		
		print("6: " + pets.remove(cymric)); // 没有该元素,移除的话,返回false
		
		// Must be the exact object:
		print("7: " + pets.remove(p)); // 移除成功
		
		print("8: " + pets);
		
		pets.add(3, new Mouse()); // Insert at an index, 在序列3上插入一个元素
		print("9: " + pets);
		
		List<Pet> sub = pets.subList(1, 4); // 截取子序列,包括1,不包括4
		print("10: subList = " + sub);
		print("10: " + pets.containsAll(sub)); // 是否包含子序列
		
		Collections.sort(sub); // In-place sort, 对元素排序
		print("11: sorted subList = " + sub);
		// Order is not important in containsAll():
		print("11: " + pets.containsAll(sub)); // 排序与否 不影响 包含关系
		
		Collections.shuffle(sub, rand); // 将元素序号打乱-shuffle-存储
		print("12: shuffled subList = " + sub);
		print("12: " + pets.containsAll(sub)); // 打乱之后 还是 不 影响 包含关系
		
		List<Pet> copy = new ArrayList<Pet>(pets);
		sub = Arrays.asList(pets.get(1), pets.get(4));
		print("13: sub = " + sub);
		print("13: copy = " + copy);
		copy.retainAll(sub); // 求交集
		print("13: copy.retainAll(sub) 方法求交集并赋值给 copy, copy = " + copy);
		
		copy = new ArrayList<Pet>(pets); // 获得一个全新的 拷贝。注意是全新。
		print("14: befor copy = " + copy);
		copy.remove(2); // Remove by index,移除序号为2上的元素
		print("14: after remove(2), copy =  " + copy);
		
		print("15: before copy = " + copy);
		print("15: before sub = " + sub);
		copy.removeAll(sub); // 移除 copy中 copy 与 sub 的交集元素
		print("15: after copy.removeAll(sub)-补集: " + copy);
		
		copy.set(1, new Mouse()); // 修改某个序号上的元素
		print("16: copy.set(1, new Mouse())-修改某序号1上的元素, copy = " + copy);
		
		copy.addAll(2, sub); // 将子序列中的元素 从 给定序号 插入到 原始序列中
		print("17: copy.addAll(2, sub)-在序号2上插入sub子集 , copy = " + copy);
		
		print("18: pets.isEmpty() = " + pets.isEmpty());
		
		pets.clear(); // 删除所有元素
		print("19: pets.clear(), pets = " + pets);
		
		print("20: pets.isEmpty() = " + pets.isEmpty()); // 序列是否为空
		
		pets.addAll(Pets.arrayList(4)); // collection实例的 addAll() 方法 添加元素
		print("21: after pets.addAll(Pets.arrayList(4)), pets = " + pets);
		
		Object[] o = pets.toArray(); // 列表转为 数组
		print("22: after Object[] o = pets.toArray(), p[3] = " + o[3]);
		
		Pet[] array = new Pet[4];
		// array = (Pet[]) pets.toArray(); // 
		pets.toArray(array); //
		print("23: after Pet[] array = new Pet[4], pets.toArray(array), array[3].id() = " + array[3].id());
	}
}  

1: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug]
2: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamster]
3: true
4: Cymric 2
5: -1
6: false
7: true
8: [Rat, Manx, Mutt, Pug, Cymric, Pug]
9: [Rat, Manx, Mutt, Mouse, Pug, Cymric, Pug]
10: subList = [Manx, Mutt, Mouse]
10: true
11: sorted subList = [Manx, Mouse, Mutt]
11: true
12: shuffled subList = [Mouse, Manx, Mutt]
12: true
13: sub = [Mouse, Pug]
13: copy = [Rat, Mouse, Manx, Mutt, Pug, Cymric, Pug]
13: copy.retainAll(sub) 方法求交集并赋值给 copy, copy = [Mouse, Pug]
14: befor copy = [Rat, Mouse, Manx, Mutt, Pug, Cymric, Pug]
14: after remove(2), copy =  [Rat, Mouse, Mutt, Pug, Cymric, Pug]
15: before copy = [Rat, Mouse, Mutt, Pug, Cymric, Pug]
15: before sub = [Mouse, Pug]
15: after copy.removeAll(sub)-补集: [Rat, Mutt, Cymric, Pug]
16: copy.set(1, new Mouse())-修改某序号1上的元素, copy = [Rat, Mouse, Cymric, Pug]
17: copy.addAll(2, sub)-在序号2上插入sub子集 , copy = [Rat, Mouse, Mouse, Pug, Cymric, Pug]
18: pets.isEmpty() = false
19: pets.clear(), pets = []
20: pets.isEmpty() = true
21: after pets.addAll(Pets.arrayList(4)), pets = [Manx, Cymric, Rat, EgyptianMau]
22: after Object[] o = pets.toArray(), p[3] = EgyptianMau
23: after Pet[] array = new Pet[4], pets.toArray(array), array[3].id() = 14

【代码解说】

1)equals 方法的重要性:  retainAll()  removeAll()  或  contains()  或 indexof()  或 remove() 等方法, 都需要用到 equals() 方法;

2)List 方法列表:

2.1)add方法:add(h) ,   add(3, new Mouse()),  addAll(2, sub),  

2.2)contains方法:contains(h),  containsAll(sub),

2.3)remove方法: remove(h), remove(2),list.removeAll(sub)-求补集方法=>list,  

2.4)get方法: get(2), 

2.5)查找序号 indexof方法:  indexOf(p), 

2.6)求交集方法retainAll:copy.retainAll(sub)-交集,

2.7)修改元素方法set: set(1, new Mouse())

2.8)子集方法subList() :   List<Pet> sub = pets.subList(1, 4),   

2.9)是否为空 isEmpty() 方法: pets.isEmpty();

2.10)List转数组方法 toArray : pets.toArray();

或 Pet[] array = new Pet[4];
// array = (Pet[]) pets.toArray(); // 
pets.toArray(array); //

2.12)清空List所有元素方法 clear:pets.clear(); // 删除所有元素

2.11)其他方法:

Collections的排序方法sort: Collections.sort(sub) , 

Arrays.asList() 数组转List方法:   sub = Arrays.asList(pets.get(1), pets.get(4)), 

(Attention——List转数组 和 数组转List;交集方法和补集方法)


【11.6】迭代器

1)迭代器的目的: 遍历并选择序列中的对象,而不必关心该序列底层的结构;

2)如何遍历?

iterator() 方法返回一个 Iterator 对象;

next() 方法获的下一个元素;

hasNext() 检查序列中是否还有元素;

remove() 把迭代器返回的元素删除;

【迭代器的荔枝】Iterator 仅能单向移动

// 遍历 List 等容器的 三种方式
public class SimpleIteration {
	public static void main(String[] args) {
		List<Pet> pets = Pets.arrayList(12);
		Iterator<Pet> it = pets.iterator(); // 迭代器对象
		
		while (it.hasNext()) { // 是否还有元素 
			Pet p = it.next(); // 获取下一个元素
			System.out.print(p.id() + ":" + p + " ");
		}
		System.out.println();
		
		for (Pet p : pets) // foreach 循环
			System.out.print(p.id() + ":" + p + " ");
		System.out.println();
		
		it = pets.iterator();
		for (int i = 0; i < 6; i++) { // for 循环
			it.next();
			it.remove(); // 调用 remove() 方法前,必须先 调用 next() 元素;
		}
		System.out.println(pets); // 输出 剩下的6个元素,因为当前的游标在 序号6那个位置
	}
}  
// 打印结果: 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster 
[Pug, Manx, Cymric, Rat, EgyptianMau, Hamster]


【迭代器的威力-荔枝】

/* Iterator的真正威力: 能够将遍历序列的操作 与序列底层的结构相分离。 */
/* 有时会说: 迭代器统一了 对容器的访问方式。 */
public class CrossContainerIteration {
	public static void display(Iterator<Pet> it) {// Iterator 参数
		while (it.hasNext()) {
			Pet p = it.next();
			System.out.print(p.id() + ":" + p + " ");
		}
		System.out.println();
	}

	public static void main(String[] args) {
		ArrayList<Pet> pets = Pets.arrayList(8);
		LinkedList<Pet> petsLL = new LinkedList<Pet>(pets);
		HashSet<Pet> petsHS = new HashSet<Pet>(pets);
		TreeSet<Pet> petsTS = new TreeSet<Pet>(pets);
		
		display(pets.iterator());
		display(petsLL.iterator());
		display(petsHS.iterator());
		display(petsTS.iterator());
	}
}
// 打印结果: 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
5:Cymric 2:Cymric 7:Manx 1:Manx 3:Mutt 6:Pug 4:Pug 0:Rat 


【11.6.1】ListIterator(可双向移动的迭代器)

(干货——Iterator 只能单向移动,ListIterator 能够双向移动)

1)获得ListIterator对象: 调用 List 的 listIterator() 方法产生一个指向 List的 ListIterator ,还可以调用 listIterator(n) 创建一个一开始就指向列表索引为 n 的元素处的 ListIterator;

【ListIterator的荔枝】

public class ListIteration { // ListIterator 迭代器可双向移动.
	public static void main(String[] args) {
		List<Pet> pets = Pets.arrayList(8);
		ListIterator<Pet> it = pets.listIterator();
		
		while (it.hasNext()) // 前向移动
			System.out.print(it.next() + ", " + it.nextIndex() + ", " + it.previousIndex() + "; ");
		System.out.println();
		
		while (it.hasPrevious()) // 后向移动 (所以 ListIterator 是双向移动)
			System.out.print(it.previous().id() + " ");
		System.out.println();
		System.out.println(pets);
		// 创建一个 一开始就指向索引 为 3 的元素处的 ListIteraor.
		it = pets.listIterator(3);
		while (it.hasNext()) {
			it.next();  // next()方法是 先加 后 返回 值;
			it.add(Pets.randomPet()); // 从索引==3处开始添加元素
		}
		System.out.println(pets);
	}
} 
/* public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
    throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
    throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];  // next()方法是 先加 后 返回 值;
}
*/
// 打印结果: 
Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6, 5; Pug, 7, 6; Manx, 8, 7; 
7 6 5 4 3 2 1 0 
[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
[Rat, Manx, Cymric, Mutt, Cymric, Pug, Rat, Cymric, EgyptianMau, Pug, Hamster, Manx, EgyptianMau]

【11.7】LinkedList

1)基本原理: LinkedList 底层用 链表实现,所以它擅长于 元素的移除 和 添加, 但查找元素的速度较慢;

2)LinkedList的特性: 还添加了 可以使其用作栈, 队列或 双端队列的方法;

3)LinkedList 方法列表:

3.1)返回第一个元素: getFirst() 和 element() 方法, peek()方法;如果list 为空,则getFirst() 和 element() 方法抛出异常 NoSuchElementException;peek() 方法不同的是 当列表为空时, 会返回 null;

3.2)移除第一个元素: removeFilrst() 和 remove() 方法, poll() 方法, 如果list 为空, 则 removeFirst() 和 remove() 方法抛出异常 NoSuchElementException;而poll() 不同的是, 当列表为空时 返回 null;

3.3)插入第一个元素: addFirst() 方法;

3.4)插入最后一个元素: offer() 方法 == add()方法 和 addLast() 方法;

3.4)删除最后一个元素并返回删除的元素:removeLast() 方法;若list 为null,则抛出异常NoSuchElementException

【LinkedList 荔枝】

// LinkedList 的方法列表
// 利用LinkedList可实现 栈,队列,双端队列
public class LinkedListFeatures {
	public static void main(String[] args) {
		LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5));
		print("0:pets = " + pets);
		
		print("1:pets.getFirst() =  " + pets.getFirst()); // 获取 第一个元素,列表为空 抛异常
		print("1:pets.element() = " + pets.element()); // 获取 第一个元素, 列表为空 抛异常		
		
		print("2:pets.peek() = " + pets.peek()); // 获取 第一个元素,列表为空 不抛异常 返回null
		
		print("3:pets.remove() = " + pets.remove()); // 删除第一个元素并返回删除的元素,为空抛异常 
		print("3:pets.removeFirst() = " + pets.removeFirst()); // 删除第一个元素并返回删除的元素,为空抛异常
		
		print("4:pets.poll() = " + pets.poll()); // 删除第一个元素,为空不抛异常 返回null
		print("4:pets = " + pets);
		
		pets.addFirst(new Rat()); // 在队首 插入
		print("5:After pets.addFirst(new Rat()), pets = " + pets);
		
		pets.offer(Pets.randomPet()); // 元素进队, 在队尾插入
		print("6:After pets.offer(Pets.randomPet()), pets = " + pets);
		
		pets.add(Pets.randomPet()); // 在队尾插入
		print("7:After pets.add(Pets.randomPet()), pets = " + pets);
		
		pets.addLast(new Hamster()); // 在队尾插入
		print("8:After pets.addLast(new Hamster()), pets = " + pets);
		
		print("9:pets.removeLast() =  " + pets.removeLast()); // 删除队尾元素
		print("9:pets = " + pets);
	}
} 

// 打印结果: 
0:pets = [Rat, Manx, Cymric, Mutt, Pug]
1:pets.getFirst() =  Rat
1:pets.element() = Rat
2:pets.peek() = Rat
3:pets.remove() = Rat
3:pets.removeFirst() = Manx
4:pets.poll() = Cymric
4:pets = [Mutt, Pug]
5:After pets.addFirst(new Rat()), pets = [Rat, Mutt, Pug]
6:After pets.offer(Pets.randomPet()), pets = [Rat, Mutt, Pug, Cymric]
7:After pets.add(Pets.randomPet()), pets = [Rat, Mutt, Pug, Cymric, Pug]
8:After pets.addLast(new Hamster()), pets = [Rat, Mutt, Pug, Cymric, Pug, Hamster]
9:pets.removeLast() =  Hamster
9:pets = [Rat, Mutt, Pug, Cymric, Pug]
【代码解说】Queue的实现, 在LinkedList的基础上 添加了 element(), offer()  peek(),  poll(),  remove()  等方法;


【11.8】Stack 栈

1)LinkedList 具有能够直接实现 栈的所有功能的方法,因此可以直接将 LinkedList 作为 栈使用;

2)基于LinkedList 的 Stack实现(强烈推荐)

// Stack 的源码实现, 不过这不是 java api 提供的,是作者自己写的
public class Stack<T> {
	java.util.Stack<Integer> s = null;
	private LinkedList<T> storage = new LinkedList<T>();

	// 入栈
	public void push(T v) {
		// 在链表表头 插入
		storage.addFirst(v);
	}
	// 返回栈顶元素,仅仅是查看,或偷看,嘿嘿
	public T peek() {
		// 获取 第一个元素,列表为空 抛异常
		return storage.getFirst(); 
	}
	// 出栈,表头出栈;
	public T pop() {
		// 删除第一个元素,为空抛异常
		return storage.removeFirst();
	}

	public boolean empty() {
		return storage.isEmpty();
	}

	public String toString() {
		return storage.toString();
	}
}

/* java.util.Stack 源码实现(不建议用) */
public class Stack<E> extends Vector<E> {
   
    public Stack() {
    }
   
    public E push(E item) {
        addElement(item);

        return item;
    }

    public synchronized E pop() { // 同步方法,性能较低,已弃用
        E       obj;
        int     len = size();

        obj = peek();
        removeElementAt(len - 1);

        return obj;
    }

    public synchronized E peek() { // 同步方法,性能较低,已弃用
        int     len = size();

        if (len == 0)
            throw new EmptyStackException();
        return elementAt(len - 1);
    }

    public boolean empty() {
        return size() == 0;
    }

    public synchronized int search(Object o) { // 同步方法,性能较低,已弃用 
        int i = lastIndexOf(o);

        if (i >= 0) {
            return size() - i;
        }
        return -1;
    }

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = 1224463164541339165L;
}

【代码解说】

1)push方法: 元素进栈;

2)peek 和 pop方法: 都是返回栈顶元素,不过 peek仅仅是返回,而pop方法移除并返回栈顶元素;

【java.util.Stack荔枝】

public class StackTest {
	public static void main(String[] args) {
		Stack<String> stack = new Stack<String>();
		for (String s : "My dog has fleas".split(" "))
			stack.push(s);
		while (!stack.empty())
			System.out.print(stack.pop() + " ");
	}
} 

// 打印结果: 
fleas has dog My 

【11.9】Set 不保存重复元素

1)查找操作: 成了 Set中最重要的操作,因此你通常都会用 HashSet 来实现, 能够快速查找;

2)Set的基本原理: Set 就是 Collection,只是行为不同而已;Set是基于对象的值来确定归属性的;

【Set荔枝】

// HashSet 以最快的查询速度进行 存储 (比较)
public class SetOfInteger { 
	public static void main(String[] args) {
		Random rand = new Random(47);
		Set<Integer> intset = new HashSet<Integer>();
		
		for (int i = 0; i < 10000; i++) // 10000次循环也仅有30个元素 
			intset.add(rand.nextInt(30));
		
		intset.add(100); // 无序不重复
		intset.add(10000); // 无序不重复
		intset.add(1000); // 无序不重复
		System.out.println(intset);
	}
}

// 打印结果: 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 10000, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 100, 1000]

3)3种Set对比——HashSet, TreeSet 和 LinkedHashSet 比较

3.1)HashSet:使用散列函数来确定元素的存储位置 (无序不重复)底层用 HashMap.key 实现 或 LinkedHashMap.key 实现;

3.2)TreeSet:把元素存储在 红黑树数据结构 (有序不重复) 底层用 TreeMap.key 实现;

3.3)LinkedHashSet:因为查询速度的原因也使用了散列,但是他也使用了链表来维护元素的插入顺序,且保留了 哈希快速查找的查找速度;(有序不重复);继承了 HashSet, 故底层用 LinkedHashMap.key 实现 而没有用 HashMap.key实现;

【TreeSet荔枝】

// TreeSet 对 输出结果排序  (比较)
public class SortedSetOfInteger {
	public static void main(String[] args) {
		Random rand = new Random(47);
		SortedSet<Integer> intset = new TreeSet<Integer>();
		for (int i = 0; i < 10000; i++)
			intset.add(rand.nextInt(30));
		
		intset.add(100); // 有序不重复
		intset.add(10000); // 有序不重复
		intset.add(1000); // 有序不重复
		System.out.println(intset);
	}
}  
// 打印结果: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
// , 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 100, 1000, 10000] 

【Set容器的操作荔枝】

public class SetOperations {
	public static void main(String[] args) {
		Set<String> set1 = new HashSet<String>();
		Collections.addAll(set1, "A B C D E F G H I J K L".split(" "));
		print("set1 = " + set1);
		set1.add("M");
		print("H: set1.contains(\"H\") = " + set1.contains("H")); 
		print("N: set1.contains(\"N\") = " + set1.contains("N")); // set 是否包含 元素
		
		Set<String> set2 = new HashSet<String>();
		Collections.addAll(set2, "H I J K L".split(" "));
		print("\nset2 = " + set2);
		print("set2 in set1: set1.containsAll(set2) = " + set1.containsAll(set2)); // set1 是否包含 set2。 。集合的包含。
		
		set1.remove("H"); // 异常元素 H 
		print("\nset1 = " + set1);
		print("set2 in set1: set1.containsAll(set2) = " + set1.containsAll(set2));
		
		print("\nbefore: set1 = " + set1);
		print("before: set2 = " + set2);
		set1.removeAll(set2); // 移除 set1 和 set2的 交集
		print("after: set2 removed from set1: set1.removeAll(set2), set1 = " + set1);
		
		Collections.addAll(set1, "X Y Z".split(" ")); // 添加数组元素 到 set1.
		print("\n'X Y Z' added to set1: Collections.addAll(set1, \"X Y Z\".split(\" \")), set1 = " + set1);
	}
} 
// 打印结果: 
set1 = [A, B, C, D, E, F, G, H, I, J, K, L]
H: set1.contains("H") = true
N: set1.contains("N") = false

set2 = [H, I, J, K, L]
set2 in set1: set1.containsAll(set2) = true

set1 = [A, B, C, D, E, F, G, I, J, K, L, M]
set2 in set1: set1.containsAll(set2) = false

before: set1 = [A, B, C, D, E, F, G, I, J, K, L, M]
before: set2 = [H, I, J, K, L]
after: set2 removed from set1: set1.removeAll(set2), set1 = [A, B, C, D, E, F, G, M]

'X Y Z' added to set1: Collections.addAll(set1, "X Y Z".split(" ")), set1 = [A, B, C, D, E, F, G, M, X, Y, Z]

【TreeSet荔枝】

/* 将读取的文件内容分割为多个不重复单词,并存储到 TreeSet容器中 */
public class UniqueWords {
	public static void main(String[] args) {
		Set<String> words = new TreeSet<String>(new TextFile(
				"src/chapter11/UniqueWords.java", "\\W+"));
		System.out.println(words);
	}
} 
// Read a file, split by any regular expression:
	public TextFile(String fileName, String splitter) {
		super(Arrays.asList(read(fileName).split(splitter)));
		// Regular expression split() often leaves an empty
		// String at the first position:
		if (get(0).equals(""))
			remove(0);
	}
// 打印结果(有序集合): 
[Set, String, System, TextFile, TreeSet, UniqueWords, W, args, chapter11, class, import, java, main, mindview, net, new, out, package, println, public, src, static, util, void, words]
【代码解说】TreeSet 输出的结果是有序的,且按照 字典序排序的,因此大写和小写字母 划分到了不同的组中(注意: 大写字母的ASCII码 小于 小写字母的 ASCII 码)

【如何让 TreeSet 使用字母序 对 元素进行排序 ?】 

向 TreeSet 构造器传入 String.CASE_INSENTIVE_ORDER  比较器;


【把比较器传入 TreeSet构造器的荔枝】

public class UniqueWordsAlphabetic {
	public static void main(String[] args) {
		Set<String> words = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
		words.addAll(new TextFile("src/chapter11/SetOperations.java", "\\W+"));
		System.out.println(words);
	}
}

public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();
private static class CaseInsensitiveComparator
            implements Comparator<String>, java.io.Serializable { // 这个比较器 是 String$CaseInsensitiveComparator 是 String类的静态内部类
        // use serialVersionUID from JDK 1.2.2 for interoperability
        private static final long serialVersionUID = 8575799808933029326L;

        public int compare(String s1, String s2) {
            int n1 = s1.length();
            int n2 = s2.length();
            int min = Math.min(n1, n2);
            for (int i = 0; i < min; i++) {
                char c1 = s1.charAt(i);
                char c2 = s2.charAt(i);
                if (c1 != c2) {
                    c1 = Character.toUpperCase(c1);
                    c2 = Character.toUpperCase(c2);
                    if (c1 != c2) {
                        c1 = Character.toLowerCase(c1);
                        c2 = Character.toLowerCase(c2);
                        if (c1 != c2) {
                            // No overflow because of numeric promotion
                            return c1 - c2;
                        }
                    }
                }
            }
            return n1 - n2;
        }

        /** Replaces the de-serialized object. */
        private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
    }
// 打印结果: 
[A, add, addAll, added, after, args, B, before, C, chapter11, class, Collections, contains, containsAll, D, E, F, from, G, H, HashSet, I, import, in, J, java, K, L, M, main, mindview, N, nbefore, net, new, nset1, nset2, package, Print, public, remove, removeAll, removed, Set, set1, set2, SetOperations, split, static, String, to, util, void, X, Y, Z]

【总结】字典序和字母序的区别在于: 字典序按照ASCII 码来比较; 字母序在忽略字母大小写的基础上用 ASCII码来比较;


【11.10】Map

//  HashMap(以最快的查找速度进行存储) 的荔枝.
public class Statistics {
	public static void main(String[] args) {
		Random rand = new Random(47);
		Map<Integer, Integer> m = new HashMap<Integer, Integer>();
		for (int i = 0; i < 10000; i++) {
			int r = rand.nextInt(20); // 产生20以内的随机数
			Integer freq = m.get(r); // 若没有key 对应的value 则返回 null
			m.put(r, freq == null ? 1 : freq + 1); // 发生了对 Integer 的装箱和拆箱
		}
		System.out.println(m);
	}
}

// 打印结果: 
{0=481, 1=502, 2=489, 3=508, 4=481, 5=503, 6=519, 7=471, 8=468, 9=549, 10=513, 11=531, 12=521, 13=506, 14=477, 15=497, 16=533, 17=509, 18=478, 19=464}
2)Map.containsKey()方法 和 containsValue()方法

// Map.containsKey() 和 Map.containsValue() 的区别
public class PetMap {
	public static void main(String[] args) {
		Map<String, Pet> petMap = new HashMap<String, Pet>();
		petMap.put("My Cat", new Cat("Molly"));
		petMap.put("My Dog", new Dog("Ginger"));
		petMap.put("My Hamster", new Hamster("Bosco"));
		print(petMap);
		
		Pet dog = petMap.get("My Dog");
		print(dog);
		print(petMap.containsKey("My Dog")); // 是否包含该key
		print(petMap.containsValue(dog)); // 是否包含该value
	}
} 
// 打印结果:
{My Dog=Dog Ginger, My Cat=Cat Molly, My Hamster=Hamster Bosco}
Dog Ginger
true
true
【代码解说】容器很容易就扩展到 多维, 只需要把值设置到 map中,就可以构成强大的数据结构:最好的证明就是一般框架分前后台,只要定义好输入或输出接口,也就是map的key;则前台通过json 字符串传入相应的字段值,然后后台也通过相应的键值获取value以进行前后台的数据交互;不管你数据维度多么复杂,都是 a piece of cake.

public class MapOfList {
	public static Map<Person, List<? extends Pet>> petPeople = new HashMap<Person, List<? extends Pet>>();
	static {
		petPeople.put(new Person("Dawn-1"),
				Arrays.asList(new Cymric("Molly1-1"), new Mutt("Spot1-2")));
		
		petPeople.put(new Person("Kate-2"), 
				Arrays.asList(new Cat("Shackleton2-1"),
				new Cat("Elsie May2-2"), new Dog("Margrett2-3")));
		
		petPeople.put(new Person("Marilyn-3"), 
				Arrays.asList(new Pug("Louie aka Louis Snorkelstein Dupree3-1"), 
						new Cat("Stanford aka Stinky el Negro3-2"), new Cat("Pinkola3-3")));
		
		petPeople.put(new Person("Luke-4"),
				Arrays.asList(new Rat("Fuzzy4-1"), new Rat("Fizzy4-2")));
		
		petPeople.put(new Person("Isaac-5"), Arrays.asList(new Rat("Freckly5-1")));
	}
	public static void main(String[] args) {
		print("petPeople.keySet() = " + petPeople.keySet()); // 键set
		print("petPeople.values() = " + petPeople.values()); // 值set
		print("petPeople.entrySet() = " + petPeople.entrySet()); // 键值对set
		
		for (Person person : petPeople.keySet()) {
			print(person + " has:"); // map.key
			for (Pet pet : petPeople.get(person)) // map.value 是一个 list,遍历整个list
				print("    " + pet);
		}
	}
} 
// 打印结果: 
petPeople.keySet() = [Person Luke-4, Person Marilyn-3, Person Kate-2, Person Isaac-5, Person Dawn-1]
petPeople.values() = [[Rat Fuzzy4-1, Rat Fizzy4-2], [Pug Louie aka Louis Snorkelstein Dupree3-1, Cat Stanford aka Stinky el Negro3-2, Cat Pinkola3-3], [Cat Shackleton2-1, Cat Elsie May2-2, Dog Margrett2-3], [Rat Freckly5-1], [Cymric Molly1-1, Mutt Spot1-2]]
petPeople.entrySet() = [Person Luke-4=[Rat Fuzzy4-1, Rat Fizzy4-2], Person Marilyn-3=[Pug Louie aka Louis Snorkelstein Dupree3-1, Cat Stanford aka Stinky el Negro3-2, Cat Pinkola3-3], Person Kate-2=[Cat Shackleton2-1, Cat Elsie May2-2, Dog Margrett2-3], Person Isaac-5=[Rat Freckly5-1], Person Dawn-1=[Cymric Molly1-1, Mutt Spot1-2]]
Person Luke-4 has:
    Rat Fuzzy4-1
    Rat Fizzy4-2
Person Marilyn-3 has:
    Pug Louie aka Louis Snorkelstein Dupree3-1
    Cat Stanford aka Stinky el Negro3-2
    Cat Pinkola3-3
Person Kate-2 has:
    Cat Shackleton2-1
    Cat Elsie May2-2
    Dog Margrett2-3
Person Isaac-5 has:
    Rat Freckly5-1
Person Dawn-1 has:
    Cymric Molly1-1
    Mutt Spot1-2 

【代码解说】:map 可以返回她的键set 或 值 set,或键值对set;


【11.11】 Queue 队列

1)队列基础知识:队列是一个容器,从队列的一端插入元素,从另一端取出元素,并且插入顺序和取出顺序是相同的;

2)LinkedList 支持 Queue:LinkedList 提供了方法以支持队列的行为, LinkedList 实现了 Queue 接口,故LinkedList 可以作为 Queue来使用;


【Queue 由 LinkedList 来实现的 荔枝】

/*
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>
*/
// Queue 由 LinkedList 来实现的 荔枝
public class QueueDemo {
	public static void printQ(Queue queue) {
		while (queue.peek() != null) // peek() 获取第一个元素,列表为空,返回null,不会抛异常
			System.out.print(queue.remove() + " ");
		System.out.println();
	}

	public static void main(String[] args) {
		// LinkedList 是 Queue的子类
		// 确切地说, LinkedList 是 Deque 的实现类,而Deque 是 Queue的子类
		Queue<Integer> queue = new LinkedList<Integer>();
		Random rand = new Random(47);
		for (int i = 0; i < 10; i++)
			queue.offer(rand.nextInt(i + 10)); // offer() == add() 方法,元素进队,在队尾插入
		printQ(queue);
		
		Queue<Character> qc = new LinkedList<Character>();
		for (char c : "Brontosaurus".toCharArray())
			qc.offer(c);
		printQ(qc);
		
		LinkedList<String> mylist = new LinkedList<>(); // 容器为空,因为没有元素
		mylist.element(); // LinkedList.element() == getFirst() 方法;为空抛异常
	}
}  
// 打印结果: 
8 1 1 1 5 14 3 1 0 1 
B r o n t o s a u r u s 
Exception in thread "main" java.util.NoSuchElementException
	at java.util.LinkedList.getFirst(LinkedList.java:244)
	at java.util.LinkedList.element(LinkedList.java:663)
	at chapter11.QueueDemo.main(QueueDemo.java:33)
3)Queue 方法列表,和 LinkedList 方法雷同; 

1)插入尾部:offer() == add() 方法

2)不删除头部并返回头部: peek() 当queue为空返回空, element() ==getFirst() 抛出 NoSuchElementException;

3)删除头部并返回头部: poll() 当queue为空返回空, remove() 抛出NoSuchElementException;


【11.11.1】 PriorityQueue 优先级队列

1)优先级队列: 声明下一个弹出元素是最需要的元素(具有最高的优先级);

【荔枝】优先级队列 PriorityQueue 荔枝(默认优先级排序规则 和 自定义优先级排序规则)

// 优先级队列 PriorityQueue 荔枝(默认优先级排序规则 和 自定义优先级排序规则)
public class PriorityQueueDemo {
	public static void main(String[] args) {
		PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
		Random rand = new Random(47);
		for (int i = 0; i < 10; i++)
			priorityQueue.offer(rand.nextInt(i + 10)); // 入队,插入队尾
		QueueDemo.printQWithStr("默认是自然排序 == ASCII排序规则, priorityQueue = ", priorityQueue); // 默认是自然排序 == ASCII排序规则

		List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2,
				3, 9, 14, 18, 21, 23, 25); // 不可变的列表
		priorityQueue = new PriorityQueue<Integer>(ints);
		QueueDemo.printQWithStr("默认是自然排序 == ASCII排序规则, priorityQueue = ", priorityQueue);
		
		// 自定义排序规则
		priorityQueue = new PriorityQueue<Integer>(ints.size(),
				Collections.reverseOrder()); // 优先级逆序队列.
		priorityQueue.addAll(ints); // 添加整个List 元素
		QueueDemo.printQWithStr(" 自定义排序规则 == 优先级逆序队列 Collections.reverseOrder(), priorityQueue = ", priorityQueue);

		String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
		List<String> strings = Arrays.asList(fact.split("")); // 注意这种分割方式和分割结果
		PriorityQueue<String> stringPQ = new PriorityQueue<String>(strings);
		QueueDemo.printQWithStr("默认是自然排序 == ASCII排序规则, stringPQ = ", stringPQ);
		
		// 自定义排序规则
		stringPQ = new PriorityQueue<String>(strings.size(),
				Collections.reverseOrder()); // 优先级逆序队列.
		stringPQ.addAll(strings);
		QueueDemo.printQWithStr(" 自定义排序规则 == 优先级逆序队列 Collections.reverseOrder(), stringPQ = ", stringPQ);

		Set<Character> charSet = new HashSet<Character>();
		for (char c : fact.toCharArray())
			charSet.add(c); // Autoboxing 自动装箱
		PriorityQueue<Character> characterPQ = new PriorityQueue<Character>(charSet);
		QueueDemo.printQWithStr("HashSet构造的PriorityQueue,并ASCII排序规则后, characterPQ = ", characterPQ);
	}
}  

// 打印结果; 
默认是自然排序 == ASCII排序规则, priorityQueue = 0 1 1 1 1 1 3 5 8 14 (优先级重复)
默认是自然排序 == ASCII排序规则, priorityQueue = 1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25 (优先级重复)
 自定义排序规则 == 优先级逆序队列 Collections.reverseOrder(), priorityQueue = 25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1 (同上)
默认是自然排序 == ASCII排序规则, stringPQ =       A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W (同上)
 自定义排序规则 == 优先级逆序队列 Collections.reverseOrder(), stringPQ = W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A       
HashSet构造的PriorityQueue,并ASCII排序规则后, characterPQ =   A B C D E F H I L N O S T U W (优先级不重复)


【11.12】Collection 和 Iterator

1)Collection 是描述所有容器共性的根接口。 容器之间的共性都是通过 迭代器来达成的。

2)要实现 Collection 接口就需要 提供Iterator() 方法;

【荔枝】容器的两种遍历方式

// 容器的两种遍历方式: 接口遍历 和 迭代器遍历(其遍历方式 与 底层容器 解耦合).
public class InterfaceVsIterator {
	public static void display(Iterator<Pet> it) { // 参数是 Iteraotr 迭代器类型 
		while (it.hasNext()) {
			Pet p = it.next();
			System.out.print(p.id() + ":" + p + " ");
		}
		System.out.println();
	}
	public static void display(Collection<Pet> pets) { // 参数是 Collection类型
		for (Pet p : pets) 
			System.out.print(p.id() + ":" + p + " ");
		System.out.println();
	}
	public static void main(String[] args) {
		List<Pet> petList = Pets.arrayList(8);
		Set<Pet> petSet = new HashSet<Pet>(petList);
		Map<String, Pet> petMap = new LinkedHashMap<String, Pet>();
		String[] names = ("Ralph, Eric, Robin, Lacey, "
				+ "Britney, Sam, Spot, Fluffy").split(", ");
		for (int i = 0; i < names.length; i++)
			petMap.put(names[i], petList.get(i));
		
		display(petList); // 通过 Collection接口 遍历
		display(petSet); // 通过 Collection接口 遍历
		display(petList.iterator()); // 通过 迭代器 遍历
		display(petSet.iterator()); // 通过 迭代器  遍历
		
		System.out.println(petMap);
		System.out.println(petMap.keySet());
		
		display(petMap.values()); // 通过 Collection接口 遍历
		display(petMap.values().iterator()); // 通过 迭代器 遍历
	}
}  
// 打印结果 : 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
{Ralph=Rat, Eric=Manx, Robin=Cymric, Lacey=Mutt, Britney=Pug, Sam=Cymric, Spot=Pug, Fluffy=Manx}
[Ralph, Eric, Robin, Lacey, Britney, Sam, Spot, Fluffy]
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
【其他解说】

当实现一个 Collection时,尽量使用 迭代器的方式去遍历,而不是使用 Colleciton 的 foreach 循环遍历;

【实现Collection的两种方式】

方式1)继承 AbstractCollection;

方式2)实现 Iterable接口;


【方式1:继承AbstractCollection的荔枝(不推荐,因为如果实现类必须继承其他父类的话,那岂不是很难堪吗?)

// 通过继承 AbstractCollection 的方式实现Collection(1)
public class CollectionSequence extends AbstractCollection<Pet> {
	private Pet[] pets = Pets.createArray(8);

	public int size() {
		return pets.length;
	}

	public Iterator<Pet> iterator() {
		return new Iterator<Pet>() {// 通过匿名内部类实现 迭代器设计模式
			private int index = 0;

			public boolean hasNext() {
				return index < pets.length;
			}

			public Pet next() {
				return pets[index++];
			}

			public void remove() { // Not implemented
				throw new UnsupportedOperationException();
			}
		};
	}

	public static void main(String[] args) {
		CollectionSequence c = new CollectionSequence();
		InterfaceVsIterator.display(c);
		InterfaceVsIterator.display(c.iterator());
	}
}  

// 打印结果:
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 
// 容器的两种遍历方式: 接口遍历 和 迭代器遍历(其遍历方式 与 底层容器 解耦合).
public class InterfaceVsIterator {
	 // 参数是 Iteraotr 迭代器类型 (1 首选)
	public static void display(Iterator<Pet> it) {
		while (it.hasNext()) {
			Pet p = it.next();
			System.out.print(p.id() + ":" + p + " ");
		}
		System.out.println();
	}
	// 参数是 Collection类型(不推荐,因为有可能无法通过继承AbstractCollection来实现Collection )
	public static void display(Collection<Pet> pets) { 
		for (Pet p : pets) 
			System.out.print(p.id() + ":" + p + " ");
		System.out.println();
	}

【方式2:实现 Iterable接口的荔枝(强力推荐,特别当实现类继承了其他父类而无法继承 AbstractCollection 的时候)

class PetSequence {
	protected Pet[] pets = Pets.createArray(8);
}

// 实现一个不是 Collection 的外部类. 方式2: 实现 Iterable接口.
// 主要是为了要创建一个 自定义的 迭代器来 遍历 Collection 中的元素.
public class NonCollectionSequence extends PetSequence implements Iterable<Pet>{
	@Override
	public Iterator<Pet> iterator() {
		return new Iterator<Pet>() {
			private int index = 0;

			public boolean hasNext() {
				return index < pets.length;
			}

			public Pet next() {
				return pets[index++];
			}

			public void remove() { // Not implemented
				throw new UnsupportedOperationException();
			}
		};
	}

	public static void main(String[] args) {
		NonCollectionSequence nc = new NonCollectionSequence();
		InterfaceVsIterator.display(nc.iterator());
	}
}  
// 打印结果: 0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 

方式1)迭代器的方式:参数是 Iteraotr 迭代器类型 (1 首选);

方式2)foreach 循环:参数是 Collection类型(不推荐,因为有可能无法通过继承AbstractCollection来实现Collection );

【Collection实现类的总结】当然了,条件允许的话,你既可以继承 AbstractCollection 也可以 实现 Iterable 接口,以利于代码维护;


【11.13】foreach 与 迭代器

1)foreach 遍历: foreach 不仅用于数组,也可以用于 Collection 的任何容器;


【foreach遍历容器的荔枝】

// foreach 工作的 荔枝.
public class ForEachCollections {
	public static void main(String[] args) {
		Collection<String> cs = new LinkedList<String>();
		Collections.addAll(cs, "Take the long way home".split(" "));
		for (String s : cs)
			System.out.print("'" + s + "' ");
	}
}
// 打印结果: 'Take' 'the' 'long' 'way' 'home' 

2)foreach 工作原理: 因为 java se5  引入了 Iterable 接口, 该接口包含一个能够产生 Iterator 的 iterator() 方法, 并且 Iterable 接口被 foreach 用来在 序列中移动。如果实现类实现了 Iterable,则都可以用 foreach 去遍历 该实现类的对象数组, 或以该实现类作为泛型的容器;


【foreach 遍历的荔枝(这个荔枝非常重要)】

// 只要创建了任何实现 Iterable的类,都可以将它用于 foreach 语句中.
public class IterableClass implements Iterable<String> {
	protected String[] words = ("And that is how "
			+ "we know the Earth to be banana-shaped.").split(" ");

	public Iterator<String> iterator() {
		/* 返回一个匿名内部类 */
		return new Iterator<String>() {
			private int index = 0;

			public boolean hasNext() {
				return index < words.length;
			}

			public String next() {
				return words[index++];
			}

			public void remove() { // Not implemented
				/* 注意这里抛出的异常类型UnsupportedOperationException */
				throw new UnsupportedOperationException();
			}
		};
	}

	public static void main(String[] args) {
		for (String s : new IterableClass())
			System.out.print(s + " ");
		System.out.println();
	}
}  
//  打印结果: And that is how we know the Earth to be banana-shaped. 

【foreach 用于遍历数组】

// foreach 语句也可以 应用于 遍历数组 
public class ArrayIsNotIterable {
	static <T> void test(Iterable<T> ib) {
		for (T t : ib)
			System.out.print(t + " ");
	}

	public static void main(String[] args) {
		test(Arrays.asList(1, 2, 3));
		String[] strings = { "A", "B", "C" };
		// An array works in foreach, but it's not Iterable:
		//  test(strings);
		// You must explicitly convert it to an Iterable:
		test(Arrays.asList(strings));
	}
} 
//打印结果: 1 2 3 A B C 
【代码解说】 在java se5中,大量的类都是 Iterable类型,主要包括 所有的 Collection,但是肯定不包括 Map 类型;


【foreach荔枝--打印操作系统环境变量】

/* 打印操作系统环境变量  */
public class EnvironmentVariables {
	public static void main(String[] args) {
		for (Map.Entry entry : System.getenv().entrySet()) { // entry 是一个 Set
			System.out.println(entry.getKey() + ": " + entry.getValue());
		}
	}
} 

// 打印结果: 
LOCALAPPDATA: C:\Users\pacoson\AppData\Local
PROCESSOR_LEVEL: 6
FP_NO_HOST_CHECK: NO
USERDOMAIN: pacoson-PC
LOGONSERVER: \\PACOSON-PC
JAVA_HOME: D:\java\jdk1.8.0_91
SESSIONNAME: Console
ALLUSERSPROFILE: C:\ProgramData
PROCESSOR_ARCHITECTURE: AMD64
PSModulePath: C:\Windows\system32\WindowsPowerShell\v1.0\Modules\
SystemDrive: C:
APPDATA: C:\Users\pacoson\AppData\Roaming
USERNAME: pacoson
windows_tracing_logfile: C:\BVTBin\Tests\installpackage\csilogfile.log
ProgramFiles(x86): C:\Program Files (x86)
CommonProgramFiles: C:\Program Files\Common Files
Path: C:\ProgramData\Oracle\Java\javapath;.;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\java\jdk1.8.0_91\bin;D:\Program Files (x86)\MySQL\MySQL Server 5.1\bin;C:\Program Files (x86)\MySQL\MySQL Server 5.1\bin;E:\cmb\elasticsearch\elasticsearch-5.2.0\bin;E:\cmb\elasticsearch\kibana-5.2.0-windows-x86\bin;
PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
OS: Windows_NT
windows_tracing_flags: 3
COMPUTERNAME: PACOSON-PC
PROCESSOR_REVISION: 3c03
CLASSPATH: .;D:\java\jdk1.8.0_91\lib\tools.jar;D:\java\jdk1.8.0_91\lib\dt.jar;
CommonProgramW6432: C:\Program Files\Common Files
ComSpec: C:\Windows\system32\cmd.exe
ProgramData: C:\ProgramData
ProgramW6432: C:\Program Files
HOMEPATH: \Users\pacoson
SystemRoot: C:\Windows
TEMP: C:\Users\pacoson\AppData\Local\Temp
HOMEDRIVE: C:
PROCESSOR_IDENTIFIER: Intel64 Family 6 Model 60 Stepping 3, GenuineIntel
USERPROFILE: C:\Users\pacoson
TMP: C:\Users\pacoson\AppData\Local\Temp
CommonProgramFiles(x86): C:\Program Files (x86)\Common Files
ProgramFiles: C:\Program Files
PUBLIC: C:\Users\Public
NUMBER_OF_PROCESSORS: 4
windir: C:\Windows
=::: ::\
【注意】不存在任何 从数组到 Iterable类型的 自动转换,你必须手动转换;


【11.13.1】适配器方法惯用法,适配器设计模式

1)添加一个能够产生 Iterable对象的方法:

【反向迭代器的荔枝】

// ArrayList的子类
class ReversibleArrayList<T> extends ArrayList<T> {
	public ReversibleArrayList(Collection<T> c) {
		super(c);
	}
	/* 添加一个能够产生 Iterable对象的方法  */
	public Iterable<T> reversed() {
		return new Iterable<T>() { // 匿名内部类
			// 这里会产生一个  反向迭代器
			public Iterator<T> iterator() {
				return new Iterator<T>() {
					int current = size() - 1;

					public boolean hasNext() {
						return current > -1;
					}
					public T next() {
						return get(current--);
					}
					public void remove() { // Not implemented
						throw new UnsupportedOperationException();
					}
				};
			}
		};
	}
}
public class AdapterMethodIdiom {
	public static void main(String[] args) {
		ReversibleArrayList<String> ral = new ReversibleArrayList<String>(
				Arrays.asList("To be or not to be".split(" ")));
		for (String s : ral) // foreach 循环遍历
			System.out.print(s + " ");
		System.out.println();
		
		for (String s : ral.reversed()) // 反向迭代遍历
			System.out.print(s + " ");
	}
}  
// 打印结果:
// To be or not to be 
// be to not or be To 

【多种迭代器方式的荔枝】

// 多种迭代器方式
public class MultiIterableClass extends IterableClass {
	public Iterable<String> reversed() {
		return new Iterable<String>() {
			public Iterator<String> iterator() {
				return new Iterator<String>() { // 返回一个 逆向迭代器
					int current = words.length - 1;

					public boolean hasNext() {
						return current > -1;
					}
					public String next() {
						return words[current--];
					}
					public void remove() { // Not implemented
						throw new UnsupportedOperationException();
					}
				};
			}
		};
	}
	public Iterable<String> randomized() {
		return new Iterable<String>() {
			public Iterator<String> iterator() { // 翻译一个随机混排后的迭代器
				List<String> shuffled = new ArrayList<String>(
						Arrays.asList(words));
				Collections.shuffle(shuffled, new Random(47));
				return shuffled.iterator();
			}
		};
	}
	public static void main(String[] args) {
		MultiIterableClass mic = new MultiIterableClass();
		for (String s : mic.reversed()) // 逆向迭代
			System.out.print(s + " ");
		System.out.println();
		
		for (String s : mic.randomized()) // 随机迭代
			System.out.print(s + " ");
		System.out.println();
		
		for (String s : mic) // foreach 循环
			System.out.print(s + " ");
	}
} 
// 打印结果: 
banana-shaped. be to Earth the know we how is that And 
is banana-shaped. Earth that how the be And we know to 
And that is how we know the Earth to be banana-shaped. 

【荔枝--Arrays.asList()返回的List 是否用 ArrayList 进行包装的 区别】

//  Arrays.asList()返回的List 是否用 ArrayList 进行包装的 区别.
public class ModifyingArraysAsList {
	public static void main(String[] args) {
		Random rand = new Random(47);
		Integer[] ia = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
		/* ArrayList的构造函数首先会拷贝数组,所以List引用的数组与原数组不是同一个数组  */
		System.out.println("Before shuffling: list1 = " + list1);
		Collections.shuffle(list1, rand);  
		System.out.println("After shuffling: Collections.shuffle(list1, rand), list1 = " + list1);
		System.out.println("array: Arrays.toString(ia) = " + Arrays.toString(ia));
		
		System.out.println("\n================================================\n");
		List<Integer> list2 = Arrays.asList(ia);
		/* 没有使用 ArrayList 构造函数,所以 list2 引用的数组 与 原数组 ia 是同一个数组  */
		System.out.println("Before shuffling: " + list2);
		Collections.shuffle(list2, rand);
		System.out.println("After shuffling: " + list2);
		System.out.println("array: " + Arrays.toString(ia));
	}
}  
// 打印结果 : 
Before shuffling: list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
After shuffling: Collections.shuffle(list1, rand), list1 = [4, 6, 3, 1, 8, 7, 2, 5, 10, 9]
array: Arrays.toString(ia) = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

================================================

Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
After shuffling: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]
array: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8]


【11.14】总结










  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值