1.添加一组元素(ArrayList.....子类的简单使用)
public class AddingGroup { public static void main(String[] args) { Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5)); Integer[] moreInt = {6,7,8,9,10}; //添加数组到collection collection.addAll(Arrays.asList(moreInt)); //将数组添加到collection后 Collections.addAll(collection, 11,12,13,14,15); Collections.addAll(collection, moreInt); List<Integer> list = Arrays.asList(1,2,3,4); } }
2.显示类型参数说明
- 这个bug在java 1.8的时候已经消除了
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) { List<Snow> snow1 = Arrays.asList(new Crusty(),new Slush(),new Powder()); //这里在java 1.8之前asList方法会返回Light 和Heavy List<Powder>的形式的list List<Snow> snow2 = Arrays.asList(new Light(),new Heavy()); //需要这样才能解决上面的问题,这叫做“显式类型参数说明” List<Snow> snow3 = Arrays.<Snow>asList(new Light(),new Heavy()); System.out.println(snow2.get(0)); } }
3.容器的打印格式[] {key=value}......与区别
- Collection在每个槽中只能保存一个元素,
- List:以特定的顺序保存一组元素
- Set:保存的元素不能重复
- Queue:只允许在容器的一端插入对象,并从另一端一处对象。
-
Map:每个槽内保存两个对象,即key value
- 打印结果的格式
- Collection 打印出来的内容用方括号括住,每个元素用逗号分隔,[1, 2, 3, 4, 5]
- Map的打印结果用大括号括住,键与值由等号联系(键在左,值在右){key1=1, key2=2, key5=5, key3=3, key4=4}
public class PrintingContainers { static Collection fill(Collection<String> collection){ collection.add("rat"); collection.add("cat"); //这两个元素相同,我们将看到set和list queue的不同之处 collection.add("dog"); collection.add("dog"); return collection; } static Map fill(Map<String, String> map){ map.put("rat", "Fuzzy"); map.put("cat", "Rags"); //下面这两个键值相同 map.put("dog", "Bosco"); map.put("dog", "Spot"); return map; } public static void main(String[] args) { System.out.println(fill(new ArrayList<String>())); System.out.println(fill(new LinkedList<String>())); System.out.println(fill(new HashSet<String>())); System.out.println(fill(new TreeSet<String>())); System.out.println(fill(new LinkedHashSet<String>())); System.out.println(fill(new HashMap<String,String>())); System.out.println(fill(new TreeMap<String,String>())); System.out.println(fill(new LinkedHashMap<String,String>())); } } /** * [rat, cat, dog, dog] ArrayList * [rat, cat, dog, dog] LinkedList * [rat, cat, dog] HashSet * [cat, dog, rat] TreeSet * [rat, cat, dog] LinkedHashSet * {rat=Fuzzy, cat=Rags, dog=Spot} HashMap * {cat=Rags, dog=Spot,rat=Fuzzy} TreeMap * {rat=Fuzzy, cat=Rags, dog=Spot} LinkedHashMap */
- ArrayList和LinkedList都是List类型,两者的不同之处在于执行某些类型的操作时的性能,而LinkedList包含的操作也多余ArrayList;
- HashSet是最快获取元素的方式,存储是没有顺序。
TreeSet按照比较结果的升序保存对象
LinkedHashSet按照被添加的顺序保存对象。 - Map按照键值查找对象,就像一个简单的数据库
HashMap提供了最快的查找技术,无顺序保存元素。
TreeMap 按照比较结果的升序保存键。
LinkedHashMap按照插入顺序保存键。
4.List的特性和相关方法
- ArrayList:常用于随机访问元素,但在List的中间插入和一出元素时较慢。
- LinkedList:随机访问元素较慢,但在List中间插入和删除元素快,代价低,提供了优化的顺序访问,特性集比ArrayList更大。
- List允许在它被创建后添加元素,移除元素,或自我调整尺寸。一个可修改的序列。优于数组。
- List中的相关方法 List<Pet> pets = Pets.arrayList(7);
- contains()确定某个对象是否在列表中 eg:pets.contains(h);确定h是否在pets中
- remove()移除某个对象 eg:pets.remove(h);从pets中移除h.
- indexOf()获得对象的索引 eg:int i = pets.indexOf(h);获得h在pets中的索引。
- 当确定一个元素是否属于List,发现某个元素的索引,以及从某个List中移除一个元素时,都会用到equals()方对传入方法的对象做比较。LIst的行为根据equals()的行为有所变化。
- add(index,T)在List中间插入元素eg:pets.add(3,h);,在第三个位置插入一个元素。
- subList()从一个较大的List中截取一个片段,将其结果传入containsAll()方法时,会得到true,并且顺序并不重要。
- Coolection.sort()和Collection.shuffle()重新排序。
- retainAll()方法取交集的方法,两个list 取其交集
- remove(index)使用索引移除对象,不用担心equals()方法。
- removeAll()移除所有的元素。
- set()方法 在指定的索引处用第二个参数替换整个位置的元素。replace更适合为这个方法的名字。
- addAll() 追加到尾部 addAll(index,list)在指定位置追加。
- toArray()方法,将任意的Collection转换为一个数组。这是一个重载方法,无参版本返回的是Objiec数组。
5.迭代器
- 任何容器类,都必须有某种方式可以插入元素并将它再次取回。 List的是add() 和 get()方法
如果将容器换成set的话就不能使用add()和get()方法了。如果方法临时改变容器,这个缺点
就暴露出来了。 - 如何能够不重写代码就能够应用于不同类型的容器呢? 迭代器就能够解决这个问题。
- 迭代器是一个对象,用于遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列的底层实现。 迭代器被称为轻量级对象。
- java的迭代器只能单向移动,
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+" "); } System.out.println(); for(Pet p : pets) System.out.print(p+" "); System.out.println(); it = pets.iterator(); for(int i = 0; i < 6; i++){ //移动到下一个 it.next(); //移除 it.remove(); } System.out.println(pets); } } /** * Cat Hamster Dog Pug Dog Pug Mutt Cymric EgyptianMau Manx Pet Pet * Cat Hamster Dog Pug Dog Pug Mutt Cymric EgyptianMau Manx Pet Pet * [Mutt, Cymric, EgyptianMau, Manx, Pet, Pet] */
- 不同容器的迭代器
display 不包括它所要遍历的容器的类型信息,所以迭代器统一了对容器的访问方式。public class CrossContainerIteration { public static void display(Iterator<Pet> it){ while(it.hasNext()){ Pet p = it.next(); System.out.print(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()); } } /** * Cat Hamster Dog Pug Dog Pug Mutt Cymric * Cat Hamster Dog Pug Dog Pug Mutt Cymric * Cat Hamster Dog Pug Dog Pug Mutt Cymric * Cat Cymric Dog Dog Hamster Mutt Pug Pug */
6.Listlterator
- Iterator的子类型,只能用于List的访问。
- ListIterator可以双向移动。除了next()方法外,还有previous()方法
- 可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引。
- 可以使用set()方法替换它访问过的最后一个元素。
- 可以通过listIterator(n)创建一个从一开始就指向列表索引为n的元素处的ListIterator.
public class ListIteration { public static void main(String[] args) { List<Pet> pets = Pets.arrayList(8); //获取一个ListIterator ListIterator<Pet> it = pets.listIterator(); while(it.hasNext()) //previousIndex返回当前元素上一个的索引nextIndex返回当前元素的下一个索引 System.out.print(it.next() +", " + it.nextIndex() + ", " + it.previousIndex()+"; "); System.out.println(); //前面读到了最后,现在获取前一个,然后打印出来,倒叙 while (it.hasPrevious()) System.out.print(it.previous() + " "); System.out.println(); System.out.println(pets); //获取一个从列表中第三个索引开始的iterator it = pets.listIterator(3); while (it.hasNext()) { it.next(); //替换它访问过的最后一个元素 it.set(Pets.randomPet()); } System.out.println(pets); } } /** * Cat, 1, 0; Hamster, 2, 1; Dog, 3, 2; Pug, 4, 3; Dog, 5, 4; Pug, 6, 5; Mutt, 7, 6; Cymric, 8, 7; * Cymric Mutt Pug Dog Pug Dog Hamster Cat * [Cat, Hamster, Dog, Pug, Dog, Pug, Mutt, Cymric] * [Cat, Hamster, Dog, EgyptianMau, Manx, Pet, Pet, Rodent] */
7.LinkedList
- 执行插入和移除比ArrayList更高效。
- 添加了可以使其用作栈、队列或双端队列的方法。
public class LinkedListFeatures { public static void main(String[] args) { LinkedList<Pet> pets = new LinkedList<Pet>(Pets.arrayList(5)); System.out.println(pets); //返回列表的头一个元素,而不移除它,如果list为空。。。抛出异常 System.out.println("pets.getFirst(): " + pets.getFirst()); System.out.println("pets.element(): " + pets.element()); //返回列表的头一个元素,而不移除它,如果list为空,返回空 System.out.println("pets.peek(): " + pets.peek()); //移除并返回列表的头,列表为空抛出异常 System.out.println("pets.remove(): " + pets.remove()); System.out.println("pets.removeFirst(): " + pets.removeFirst()); //移除并返回列表的头,列表为空返回空 System.out.println("pets.poll(): " + pets.poll()); System.out.println(pets); //将某个元素插入列表的头部 pets.addFirst(Pets.randomPet()); System.out.println("After addFirst():" +pets); pets.offer(Pets.randomPet()); System.out.println("After offer(): " + pets); //将某个元素插入尾部 pets.add(Pets.randomPet()); System.out.println("After add(): " + pets); pets.addLast(new Hamster()); System.out.println("After addLast(): " + pets); //移除尾部的一个元素 System.out.println("pet.removeLast(): " +pets.removeLast()); System.out.println(pets); } } /** [Cat, Hamster, Dog, Pug, Dog] pets.getFirst(): Cat pets.element(): Cat pets.peek(): Cat pets.remove(): Cat pets.removeFirst(): Hamster pets.poll(): Dog [Pug, Dog] After addFirst():[Pug, Pug, Dog] After offer(): [Pug, Pug, Dog, Mutt] After add(): [Pug, Pug, Dog, Mutt, Cymric] After addLast(): [Pug, Pug, Dog, Mutt, Cymric, Hamster] pet.removeLast(): Hamster [Pug, Pug, Dog, Mutt, Cymric] */
8.Stack
- 栈通常指“后进先出”的容器。有时栈也被称为叠加栈,因为最后“压入”栈的元素(压栈),第一个被弹出栈。然而java .util提供的Stack并不完美。
经常用来类比栈的元素是存放盘子的容器,最后放入的盘子总是被先取出。 - LinkedList具有能够直接实现栈的所有功能的方法。因为LinkedList具有addFirst方法,后放入的元素总是在前面,也就实现了压栈的功能。因此可以直接将LinkedList作为栈使用。
public class Stack<T> {//这里有一个T 是泛型,下面所有用T的都是声明这个类是传过来的类型,就像List<String>一样 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();} }
public class StackTest { public static void main(String[] args) { //创建我们刚才的栈 Stack<String> stack = new Stack<String>(); for (String s : "My dog hase fleas".split(" ")) //往栈中压入元素 stack.push(s); while(!stack.empty()) //移除栈中的元素 后进先出 System.out.println(stack.pop() + " "); } }
9.Set
- Set不保存重复的元素。
- Set中最常使用的是测试归属性(是否存在于此集合中),可以很容易的查找到某个对象是否在Set中。
正因如此查找就成了Set中最重要的操作。
- HashSet:专门对快速查找进行了优化。
- Set具有与Collection完全一样的接口,没有任何具体的额外的功能,不像前面两个不同的List,
实际上Set就是Collection,同类型,只是行为不同(继承和多态的体现)
- HashSet:无序,散列函数存储元素的方式
- TreeSet:有序,将元素存储在红-黑树数据结构中,不用发排序方式只需要传入不同的参数即可
- LinkedHashList:将元素存储在散列函数中,但是使用链表来维护元素的插入顺序。
- 基本使用:
public class SetOperations { public static void main(String[] args) { Set<String> set1 = new HashSet<String>(); //将一个数组放进set1中 Collections.addAll(set1, "A B C D E F G H I J K L".split(" ")); //添加一个元素 set1.add("M"); System.out.println("H: " + set1.contains("H")); System.out.println("N: " + set1.contains("N")); Set<String> set2 = new HashSet<String>(); Collections.addAll(set2, "H I J K L".split(" ")); //set1中存在set1吗 System.out.println("set2 in set1: " + set1.containsAll(set2)); set1.remove("H"); System.out.println("set1: " + set1); //此时set1还包含set2吗 System.out.println("set2 in set1: " + set1.containsAll(set2)); set1.removeAll(set2); //set1移除和set2相同的元素 System.out.println("set2 removed from set1: " + set1); Collections.addAll(set1, "X Y Z".split(" ")); System.out.println("'X Y Z' added to set1: " + set1); } }
10.Map
- 将对象映射到其他对象:即键值对(key-value)。
- key不可以重复,value可以重复。
- containsKey()和containsValue()两个方法测试是否包含某个键或某个值。
- 注意:Map可以返回它的键的Set,他的值的Collection,或者它的键值对的Set,
keySet()方法产生map中所有键组成的Set,它在foreach语句中被用来遍历该Map
11.Queue
- 队列和栈不同,队列是先进先出的容器,即从一端放入,从另一端取出,并且放入和取出顺序是相同的,即第一个放入的将被第一个取出。
- 队列常被当做一种可靠的将对象从程序的一个区域传输到另一个区域的途径。在并发中非常重要。
- LinkedList提供了方法支持队列的行为,它实现了Queue接口。
public class QueueDemo { public static void printQ(Queue queue){ //peek和element在不移除的情况下返回队头,但peek()方法在队列为空时返回null,element则抛出异常 while (queue.peek()!=null) { //poll和remove方法将移除并返回队头,但poll在队列为空时返回null System.out.println(queue.remove() + " "); System.out.println(); } } public static void main(String[] args) { //这儿使用了一个LinkedList Queue<Integer> queue = new LinkedList<Integer>(); Random rand = new Random(47); for(int i = 0; i < 10; i++) //offer 将一个元素插入到队尾 queue.offer(rand.nextInt(i + 10)); System.out.println(queue); Queue<Character> qc = new LinkedList<Character>(); for(char c : "Brontosaurus".toCharArray()) //offer 将一个元素插入到队尾 qc.offer(c); System.out.println(qc); } }
- peek和element在不移除的情况下返回队头,但peek()方法在队列为空时返回null,element则抛出异常
- poll和remove方法将移除并返回队头,但poll在队列为空时返回null,remove则抛出异常
- offer 将一个元素插入到队尾
-
Queue接口窄化了LinkedList方法的访问权限。
12.PriorityQueue 有优先级的队列
- 优先级队列声明下一个弹出元素时最需要的元素(具有最高的优先级)。
- 某些消息比其他消息更加重要,因此应该更快的得到处理,那么他们何时处理将与他们合适到达无关(队列是先进先出的规则)。解决这种问题的方法就是优先级队列。
- offer()方法向优先级队列插入一个对象,这个对象会在队列中排序,默认的排序是对象在队列中的自然排序,但可以通过Comparator来修改这个排序规则。
- 在调用peek()、poll()、remove()方法时,获取的元素将是队列中优先级最高的元素,而不是最先进来的元素。
<pre name="code" class="java">public class MultiIterableClass extends IterableClass{ private String[] words = ("And that is how " + "we know the Earth to be banana-shaped.").split(" "); //第一个适配方法 适应不同的foreach public Iterable<String> reversed(){ //返回一个可迭代的对象 return new Iterable<String>() { //返回一个迭代器 public Iterator<String> iterator() { return new Iterator<String>() { private int current = words.length - 1; public boolean hasNext() {return current > -1;} public String next() {return words[current --];} public void remove(){throw new UnsupportedOperationException();} }; } }; } //第二个适配方法 适应不同的foreach 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)); //返回被打乱的List中的Iterator return shuffled.iterator(); } }; } public static void main(String[] args) { MultiIterableClass mic = new MultiIterableClass(); for(String s : mic) System.out.print(s + " "); System.out.println(); for(String s : mic.randomized()) System.out.print(s + " "); } } /** * And that is how we know the Earth to be banana-shaped. * is banana-shaped. Earth that how the be And we know to */
总结:
- 数组将数字和对象联系起来,它是多为的,可以保存基本类型的数据。数组一旦生成,其容量就不能改变。
- Collection保存单一的元素,而Map保存相关联动的键值对。
有了java泛型,就可以制定容器中存放的对象类型,就不会将错误类型的对象放置到容器中,并且从容器中获取以元素不必进行类型转换。
Collection和Map都可以自动调整尺寸。
容器不能持有基本类型,但自动包装机制可以实现基本类型和其包装器之间的双向转换。 - 数组和List都是排好序的容器。
- 如果要进行大量的随机访问就是用ArrayList,如果经常从表中插入或删除元素,则应该使用LinkedList
- 各种Queue以及栈的行为,由LinkedList支持。
- HashMap用来设计快速访问;
TreeMap保持“键”始终处于排序状态,所以没有HashMap快。
LinkedHashMap保持元素的插入顺序,但也通过散列提供了快速访问能力。 - Set不能接受重复的元素。
HashSet提供了最快的查询速度
TreeSet保持元素处于排序状态。 - 新程序中不应该使用Vector、Hashtable和Stack等过时的类。
- 其实只有四种容器, Map、List、Set和Queue,他们有两到三个实现版本。
- Map和Collection之间唯一重叠的就是Map可以使用entrySet()和values()方法产生Collection.