持有对象-4
Set
Set<Integer> d=new HashSet<Integer>(); d.add(12); d.add(13); d.add(14); d.add(15); d.add(16); d.add(17); System.out.println(d);
你可以看到结果输出没有顺序,这是出于速度的考虑,HashSet使用了散列。HashSet所维护的顺序与TreeSet或者LinkedHashSet都不同,因为他们具有不用的元素实现方式。TreeSet将数据存储在红黑树数据结构中,而HashSet使用的是散列函数。LinkedHashSet因为查询速度的原因也使用了散列函数
,但是它看起来使用链表来维护元素的插入顺序。
如果你想对结果排序,一种方式是使用TreeSet代替
Random rand = new Random(47); Set<Integer> s = new TreeSet<Integer>(); for (int i = 0; i < 200; i++) { s.add(rand.nextInt(30)); } System.out.println(s);
如果你想要按照字母排序,就在构造器中传入String.CASE_INSENSITIVE_ORDER
Set<String> d = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);//按照字母排序 d.add("a"); d.add("b"); d.add("c"); d.add("d"); d.add("A"); d.add("B"); d.add("C"); d.add("D"); System.out.println(d);
Map
将对象映射到其他对象的能力是Map的极大优势
Map与数组和其他collection一样可以轻松扩展到多维,而我们只需要将其设置为map,而这些值可以使其他容器,甚至是其他map。因此,我们会很容易将容器组合起来从而 快速生成强大的数据结构。列如,假如你正在跟踪拥有多个宠物的人,你所需的只是一个map<person,List<pet>>
例如,存储一个二维数组
public ArrayList<Integer> getList() { ArrayList<Integer> s = new ArrayList<Integer>(); Random rand = new Random(47); for (int i = 0; i < 5; i++) { s.add(rand.nextInt(30)); } return s; } public static void main(String[] args) { // 存储一个多维数组 ToMap k=new ToMap(); Map<Integer, ArrayList<Integer>> m = new HashMap<Integer, ArrayList<Integer>>(); m.put(1, k.getList()); m.put(2, k.getList()); m.put(3, k.getList()); Set<Integer> keySet = m.keySet();//返回所有的键组成的set,经常被用来迭代map for(Integer a:keySet) { System.out.println(a); System.out.println(m.get(a)); } }
Queue
@Test public void ToQueue() { Queue<Integer> q=new LinkedList<Integer>(); Queue<Integer> q2=new LinkedList<Integer>(); //Random rand=new Random(47); for(int i=0;i<10;i++) {//两者都是往队列尾部插入元素,不同的时候,当超出队列界限的时候,add()方法是抛出异常让你处理,而offer()方法是直接返回false //q.offer(i); q2.offer(i); q.add(i); } System.out.println(q); System.out.println(q2); while(q.peek()!=null&&q2.peek()!=null) { System.out.println(q.remove()+" "+q2.remove()); } }
public void ToPriorityQueue() { //会自动排序,默认自然排序,且最小的值有最高的优先值 PriorityQueue<Integer> p=new PriorityQueue<Integer>(); Random rad=new Random(47); for(int i=0;i<10;i++) { p.offer(rad.nextInt(i+20)); } Set<Integer> s=new HashSet<Integer>();//会去除重复值,这样做只是为了增添一点乐趣 for(Integer i:p) { s.add(i); } System.out.println(s); PriorityQueue<Integer> p2=new PriorityQueue<Integer>(s); System.out.println(p2); while(p.peek()!=null) { System.out.println(p.remove()); }
//Collections.reverseOrder()是产生的反序的Comparator
PriorityQueue<String> p3=new PriorityQueue<String>(l.size(),Collections.reverseOrder());
p3.addAll(l);
while(p3.peek()!=null) {
System.out.println(p3.remove());
} }
Collection
Collection是描述所有序列容器共性的根接口,它可能被人为是一个复数接口,因为它是要表示其他若干个容器的共性而出现的接口
另外AbstractCollection提供了Collection的默认实现,使你可以创建AbstractCollection的子类型,而其中没有不必要的代码重复
使用接口描述的一个理由是它可以使我们能够创建更通用的代码。通过针对接口而非具体实现来编写代码,我们的代码可以应用于更多的对象类型。因此,如果我编写的方法将接受一个Collection,那么该方法就可以应用于任何实现了Collection的类,这也就使得一个新类可以选择去实现Collection接口,以便我的方法可以使用它。但是,有一点很有趣,就是我们注意到标准C++类库中并没有其容器的任何公共基类一容器之间的所有共性都是通过迭代器达成的。在Java中,遵循C++的方式看起来似乎很明智,即用迭代器而不是Collection来表示容器之间的共性。但是,这两种方法绑定到了一起,因为实现Collection就意味着需要提供iterator方法:
例如:
public static void display(Collection<Person> p){ for(Person s:p) { System.out.println(s); } }
public class AbstractCircle<E> extends AbstractCollection { @Override public Iterator<E> iterator() { // TODO Auto-generated method stub return new Iterator<E>() { @Override public boolean hasNext() { // TODO Auto-generated method stub return false; } @Override public E next() { // TODO Auto-generated method stub return null; } }; } @Override public int size() { // TODO Auto-generated method stub return 0; } }
从本例中,你可以看到,如果你实现Collection,就必须实现iterator(),并且只拿只实现iterator与继承AbstractCollection相比,花费的代价只有略微减少。但是,如果你的类已经继承了其他的类,那么你就不能再继承AbstractCollection了。在这种情况下,要实现Collection,就必须实现该接口中的所有方法。此时,继承并提供创建迭代器的能力就会显得容易得多了
Foreach与迭代器
到目前为止foreach主要应用于数组,但是它也可以应用于所有Collection对象。
在Java SE5引入新的iterable接口,该接口包含一个产生iterator的iterator方法,并且iterator接口foreach用来在序列中移动。因此如果你创建了任何实现iterator的方法,都将可以把他应用于foreach
例如:
public class GetInterable implements Iterable<String>{ private String[] words= {"Go","with","me","and","other"}; @Override public Iterator<String> iterator() { // TODO Auto-generated method stub return new Iterator<String>() { private int index=0; @Override public boolean hasNext() { // TODO Auto-generated method stub return index<words.length; } @Override public String next() { // TODO Auto-generated method stub return words[index++]; } }; } public static void main(String[] args) { for(String s:new GetInterable()) { System.out.println(s); } } }
/** * 显示所有的操作系统的环境变量 */ @Test public void getSys() { for(Map.Entry entry:System.getenv().entrySet()) { System.out.println(entry.getKey()+" : "+entry.getValue()); } }
System.getenv返回一个Map, entrySet0产 生一个 由Map.Entry的元素构成的Set, 并且这个Set是一个Iterable,因此它可以用于foreach循环。
foreach语句可以用于数组或其他任何Iterable,但是这并不意味着数组肯定也是一个Iterable,而任何自动包装也不会自动发生
public class GetArray<T> extends ArrayList<T> { public GetArray(Collection<T> c){ super(c); } public Iterable<T> reversed(){ return new Iterable<T>() { @Override public Iterator<T> iterator() { // TODO Auto-generated method stub return new Iterator<T>() { int current=size()-1; @Override public boolean hasNext() { // TODO Auto-generated method stub return current>-1; } @Override public T next() { // TODO Auto-generated method stub return get(current--); } public void remove() { throw new UnsupportedOperationException(); } }; } }; } }
public static void main(String[] args) { GetArray<String> g=new GetArray<String>(Arrays.asList(words)); for(String s:g) { System.out.println(s); } for(String s:g.reversed()) {//倒序 System.out.println(s); } }
如果直接将g对象置于foreach语句中,将得到默认的前向迭代器。但是如果在该对象上调用reversed方法就会产生不同的行为。通过这种方式,我们可以实例中添加两种适配器方法:
public class MultIterableClass extends GetInterable { /** * 反序迭代 * * @return */ public Iterable<String> reversed() { return new Iterable<String>() { @Override public Iterator<String> iterator() { // TODO Auto-generated method stub return new Iterator<String>() { int current = words.length - 1; @Override public boolean hasNext() { // TODO Auto-generated method stub return current > -1; } @Override public String next() { // TODO Auto-generated method stub return words[current--]; } public void remove() { throw new UnsupportedOperationException(); } }; } }; } public Iterable<String> randomized() { return new Iterable<String>() { @Override public Iterator<String> iterator() { // TODO Auto-generated method stub List<String> shuffled = new ArrayList<String>(Arrays.asList(words)); //对指定的列表进行随机排列,假设随机性的来源是公平的,那么所有的排列都以相等的概率发生。 Collections.shuffle(shuffled, new Random(47)); return shuffled.iterator(); } }; } public static void main(String[] args) { MultIterableClass mic = new MultIterableClass(); 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) { System.out.print(s + " "); } } }
Random rand = new Random(47); Integer[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; List<Integer> list = new ArrayList<Integer>(Arrays.asList(a)); System.out.println(list); Collections.shuffle(list, rand); System.out.println("打乱顺序:" + list); System.out.println("array: " + Arrays.toString(a)); System.out.println(); List<Integer> list2 = Arrays.asList(a);//会修改原来的数组 System.out.println(list2); Collections.shuffle(list2, rand); System.out.println("打乱顺序:" + list2); System.out.println("array: " + Arrays.toString(a));
在第一种情况中,Arrays.asList的输 出被传递给了ArrayList的构造器,这将创建一个引用a的元素的ArrayList,因此打乱这些引用不会修改该数组。但是,如果直接使用Arrays.asList(a)的结果,这种打乱就会修改a的顺序。意识到Arrays.asList产生的List对象会使用底层数组作为其物理实现是很重要的。只要你执行的操作会修改这个List,并且你不想原来的数组被修改,那么你就应该在另一个容器中创建一个副本