内容简介:《Java编程思想》笔记08------持有对象
如果一个程序包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序。然而事与愿违,通常情况下,程序在运行之前并不知道对象的数量,甚至不知道对象的类型。因此,需要引入一项新的技术,可以在任意时刻和任意位置创建任意数量的对象。 Java类库提供了一套相当完整的容器类来解决这个问题,其中基本的类型包括:List、Set、Map等,这些对象的类型也称为集合类。不同的容器有不同的特性,但它们的共性就是:都可以自动的调整自身的大小。因此,与数组不同, 在编程时我们可以将任意数量的对象放置在容器中。
一、泛型与类型安全的容器
为了便于理解下面的这个例子,首先介绍一个简单容器 ArrayList,我们可以把ArrayList当做是一个可以自动扩充自身大小的数组。
我们可以通过add()方法插入对象,可以通过get()方法访问对象,还可以通过size()方法来获取当前容器的大小。
在Java SE5之前的版本,编译器允许向容器中插入不正确的类型。
例如:要创建一个ArrayList用于放置Apple对象。如果把Orange对象也放入容器中,编译器是不会报错的。
/** * 泛型的使用 * @author LiangYu * 2018-01-11 */ public class GenericityTest { @Test public void test() throws Exception { ArrayList apples = new ArrayList(); for(int i = 0 ; i < 4 ; i++){ Apple apple = new Apple(); apple.setId(i); apples.add(apple); } //新来的同事手误,将Orange对象也放入了apples容器中 Orange orange = new Orange(); //编译器并没有报错 apples.add(orange); //遍历容器的时候,悲剧了。。 for(int i = 0 ; i < apples.size() ; i++){ Apple apple = (Apple) apples.get(i); System.out.println(apple.getId()); } } } class Apple{ private int id; public void setId(int id){ this.id = id; } public int getId(){ return id; } } class Orange{}
上面的例子可以看到,当我们把Orange对象放入盛有Apple对象的容器中的时候,编译器并没有报错,而是在运行的时候,发现问题,并抛出异常。
为了将类似的错误扼杀在摇篮中,我们可以声明 ArrayList<Apple>
,用于告诉编译器:这个容器只能盛放Apple对象,如果有人错把其他对象放入容器,那么请编译器报错提示。
@Test public void test() throws Exception { ArrayList<Apple> apples = new ArrayList<Apple>(); for(int i = 0 ; i < 4 ; i++){ Apple apple = new Apple(); apple.setId(i); apples.add(apple); } //新来的同事手误,准备将Orange对象也放入了apples容器中 Orange orange = new Orange(); //此时编译器报错,提示他不能将Orange对象放入容器中 // apples.add(orange); //遍历容器,运行正常 for(int i = 0 ; i < apples.size() ; i++){ Apple apple = apples.get(i); System.out.println(apple.getId()); } }
通过泛型,编译器可以阻止Orange对象放入apples容器中, 将运行时错误,变成了编译时错误 。
二、基本概念
Java容器类的主要用途是“保存对象”,并将其划分为两个概念:
1.Collection:一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素;Set不能有重复元素,Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
2.Map:一组成对的“键值对”对象,允许使用键来查找值。ArrayList允许使用数字来查找值。因此,在某种意义上讲,它将数字和对象关联在一起。映射表允许我们使用另一个映射来查找某个对象,它也称为“关联数组”。因为它将某些对象和另一些对象关联在了一起。或者成为“字典”,因为可以使用键对象来查找值对象。
在创建的时候就要指定所使用的精确类型,例如:
List<String> texts = new ArrayList<String>();
使用接口的目的在于如果决定去修改你的实现,只需要在创建时修改它,例如:
List<String> texts = new LinkedList<String>();
Collection接口概括了序列的概念——————一种存放对象的方式。
例如:用Integer对象填充一个Collection,然后打印容器中所有元素。
/** * 序列的概念 * @author LiangYu * 2018-01-16 */ public class CollectionTest { @Test public void test() throws Exception { Collection<Integer> numbers = new ArrayList<Integer>(); for(int i = 0 ; i < 10 ; i++){ numbers.add(i); } for(int i : numbers){ System.out.println(i); } } }
add()方法的名称就比表明它要将一个新元素加入到Collection中。 所有的Collection都可以用foreach遍历。
三、添加一组元素
在java.util包下的Arrays和Collections类中的很多实用方法可以在一个Collection中添加一组元素。
Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(可变参数),并将其转换为一个List对象。
Collections.addAll()方法接受一个Collection对象,以及一个数组或是一个用逗号分隔的列表,将元素添加到Collection中。
/** * 添加一组元素 * @author LiangYu * 2018-01-16 */ public class AddCollectionTest { @Test public void test() throws Exception { //Collection的构造器可以接受另一个Collection Collection<Integer> numbers = new ArrayList<Integer>(Arrays.asList(2,4,6,8,10)); Integer[] moreInts = {1,3,5,7,9}; //Collection.addAll()运行起来要快很多,但是它只能接受另一个Collection作为参数,不够灵活 numbers.addAll(Arrays.asList(moreInts)); //Collections.addAll()是首选方式 Collections.addAll(numbers, 11,12,13,14); Collections.addAll(numbers, moreInts); List<Integer> numberList = Arrays.asList(15,16,17,18,19,20); numberList.set(2, 21); //通过Arrays.asList()创建的Collection对象底层依旧是数组,因此,不能调整尺寸。 // numberList.add(98); 运行时报错:UnsupportedOperationException System.out.println(numbers); System.out.println(numberList); } }
总结:
- Collection的构造器可以接受另一个Collection
- Collection对象的addAll()运行起来要快很多,但是它只能接受另一个Collection作为参数,不够灵活
- Collections.addAll()是首选方式
- 通过Arrays.asList()创建的Collection对象底层依旧是数组,因此,不能调整尺寸。
Arrays.asList()方法的限制是它对所产生的List的类型做出最理想的假设,而并没有注意你会给它赋予什么类型。有时会引发问题。
四、容器的打印
必须使用Arrays.toString()方法来产生数组的可打印表示。打印容器不需要任何帮助。
eg:
/** * Collection的打印 * @author LiangYu * 2018-01-16 */ public class CollectionPrintTest { @Test public void test() throws Exception { System.out.println("ArrayList:"+fill(new ArrayList<String>())); System.out.println("LinkedList:"+fill(new LinkedList<String>())); System.out.println("HashSet:"+fill(new HashSet<String>())); System.out.println("TreeSet:"+fill(new TreeSet<String>())); System.out.println("LinkedHashSet:"+fill(new LinkedHashSet<String>())); 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>())); } Collection fill(Collection<String> collection){ collection.add("dog"); collection.add("cat"); collection.add("fish"); collection.add("dog"); return collection; } Map fill(Map<String,String> map){ map.put("cat", "Apache"); map.put("dog", "Cookie"); map.put("fish", "Tom"); map.put("dog", "July"); return map; } }
结论:
1.Collection在每一个“槽”里面只能保存一个元素,此类容器包括:List(以特定的顺序保存一组元素)、Set(不能重复)、Queue(只允许容器的一端插入元素,另一端移除元素); Map在每一个“槽”里面保存两个元素,分别是元素的键和它对应的值。
2.ArrayList和LinkedList都是List类型,按照被插入的顺序保存元素。不同之处:
(1)在执行某些类型的操作时性能不同
(2)LinkedList包含的操作多于ArrayList
3.HashSet和TreeSet、LinkedHashSet都是Set类型,输出显示在Set中。每一个相同的项只能保存一次。HashSet拥有最快的获取元素方式;TreeSet按照比较结果升序保存对象;LinkedHashSet按照被添加的顺序保存对象。
4.HashMap和TreeMap、LinkedHashMap都是Map类型。Map是通过键来查找对象。对于每一个键,Map只存储一次。HashMap提供了最快的查找技术;TreeMap按照比较结果升序保存键;LinkedHashMap根据插入顺序保存键,同时保留了HashMap的查询速度。
五、List
List可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法,使得可以在List中间插入和移除元素。
有两种类型的List:
- 基本的ArrayList,随机访问元素比较快,但是在List的中间插入和移除元素时较慢。
- LinkedList:可以用较低代价进行插入和移除操作,优化了顺序访问,在随机访问方面比较慢。
/** * ArrayList和LinkedList的使用 * @author LiangYu * 2018-01-16 */ public class ListTest { @Test public void test() throws Exception { List<Pet> pets = new ArrayList<Pet>(Arrays.asList(new Pet(),new Dog(),new OrangeCat(),new Mouse(),new BlueCat(),new Dog(),new WhiteMouse())); System.out.println("1:"+pets); BlueMouse blueMouse = new BlueMouse(); //添加元素 pets.add(blueMouse); System.out.println("2:"+pets); System.out.println("3:"+pets.contains(blueMouse)); //判断元素是否存在 pets.remove(blueMouse); //移除元素 Pet p = pets.get(2); //根据索引获取元素 System.out.println("4:"+p+" "+pets.indexOf(p)); //获取元素的索引 Pet orangeCat = new OrangeCat(); System.out.println("5:"+pets.indexOf(orangeCat)); //如果元素不存在,索引返回-1 System.out.println("6:"+pets.remove(orangeCat)); //如果元素不存在,remove方法返回false System.out.println("7:"+pets.remove(p)); //移除元素成功,remove方法返回true System.out.println("8:"+pets); pets.add(1,new Mouse()); //在指定位置添加对象 System.out.println("9:"+pets); List<Pet> sub = pets.subList(1, 5); //截取集合 System.out.println("subList:"+sub); System.out.println("10:"+pets.containsAll(sub)); //判断某集合是不是该集合的子集 Collections.shuffle(sub,new Random(47)); //打乱排序 System.out.println("shuffle:"+sub); List<Pet> petsCopy = new ArrayList<Pet>(pets); //拷贝一个元素内容相同的容器 sub = Arrays.asList(pets.get(2),pets.get(4)); System.out.println("sub:"+sub); petsCopy.retainAll(sub); // 获取两个集合的交集部分 System.out.println("11:"+petsCopy); petsCopy = new ArrayList<Pet>(pets); petsCopy.remove(2); System.out.println("12:"+petsCopy); petsCopy.removeAll(sub); //移除和sub交集的部分 System.out.println("13:"+petsCopy); petsCopy.set(1, new BlackMouse()); //将指定位置的元素进行修改 System.out.println("14:"+petsCopy); petsCopy.addAll(2,sub); //在指定位置插入集合 System.out.println("15:"+petsCopy); System.out.println("16:"+pets.isEmpty()); //isEmpty方法判断集合是否为空 pets.clear(); //清空集合 System.out.println("17:"+pets); System.out.println("18:"+pets.isEmpty()); Object[] objects = petsCopy.toArray(); //list转换为数组 System.out.println("19:"+objects[2]); } } class Pet{ @Override //打印时输出类名 public String toString() { return getClass().getSimpleName(); } } class Cat extends Pet{} class OrangeCat extends Cat{} class BlueCat extends Cat{} class Dog extends Pet{} class Mutt extends Dog{} class Pug extends Dog{} class SmallOrangeCat extends OrangeCat{} class Mouse extends Pet{} class WhiteMouse extends Mouse{} class BlackMouse extends Mouse{} class BlueMouse extends Mouse{}
结论:
- List允许在它被创建以后添加元素、移除元素,或者自我调节尺寸
- contains()方法用于判断某个对象是否存在于容器中
- remove()移除、add()添加
- 通过indexOf()方法用于获取指定元素的索引
- subList()可以获取列表的片段
- removeAll(List list) 从List中移除参数List中的所有元素
- retainAll(List list) 获取交集
- set(int index,Object object) 将指定位置的元素替换
- isEmpty() 判断列表是否为空; clear() 清空列表
六、迭代器
任何容器类都必须有某种方式可以插入元素并将它们再次取回。对于List而言,add()是插入元素的方法之一,get()是取出元素的方法之一。
如果原本对着List编码,但是后来发现能够把相同的代码应用于Set,此时如果代码使用了add()和get()将会影响可移植性。
迭代器的概念则可以用于达成此目的。迭代器是一个对象,它的工作是遍历并选择序列中的对象。开发者不需要关注该序列底层的结构。无论是List还是Set都可以直接使用迭代器。此外,迭代器是轻量级对象,创建它的代价非常小。因此,它的功能受到了一定的限制。
- 迭代器只能单向移动
- 通过iterator()要求容器返回一次Iterator对象。Iterator将准备好返回序列的第一个元素。
- 使用next()方法可以获取序列中的下一个元素
- 使用hasNext()判断序列中是否还有元素
- 使用remove() 将迭代器新近返回的元素删除
/** * 迭代器的使用 * @author LiangYu * 2018-01-16 */ public class IteratorTest { @Test public void test() throws Exception { List<Integer> numbers = new ArrayList<Integer>(Arrays.asList(0,1,2,3,4,5,6,7,8,9)); //获取迭代器 Iterator<Integer> iterator = numbers.iterator(); //迭代 while(iterator.hasNext()){ int num = iterator.next(); System.out.print(num); } System.out.println(); for(int i : numbers){ System.out.print(i); } System.out.println(); //重置迭代器 iterator = numbers.iterator(); for(int i = 0 ; i < 4 ; i++){ iterator.next(); iterator.remove(); } System.out.print(numbers); } }
有了迭代器,我们就不需要关心容器中元素的数量。那是hasNext()和next()关心的事。
Iterator中,remove()方法可以移除next()产生的最后一个元素,因此,调用remove()之前,首先需要调用next()
如果只是遍历List,而不修改List本身,那么foreach语法更简洁。
例:创建一个display方法,用于遍历所有内容,而不需要关注容器的确切类型
/** * 通过迭代器展示所有元素 * @author LiangYu * 2018-01-16 */ public class DisplayCollectionTest { @Test public void test() throws Exception { ArrayList<String> textList = new ArrayList<String>( Arrays.asList("Qwe","ert","yui","opasdf","ghj","kl","zx","Qwe","xcvb")); LinkedList<String> textLinkedList = new LinkedList<String>(textList); TreeSet<String> textTreeSet = new TreeSet<String>(textList); HashSet<String> textHashSet = new HashSet<String>(textList); DisplayUtil.display(textList.iterator()); DisplayUtil.display(textLinkedList.iterator()); DisplayUtil.display(textTreeSet.iterator()); DisplayUtil.display(textHashSet.iterator()); } } class DisplayUtil{ //通用方法,可以在不考虑容器类型的前提下,遍历展示所有元素 static void display(Iterator<String> texts){ while(texts.hasNext()){ String msg = texts.next(); System.out.print(msg+" "); } System.out.println(); } }
Iterator可以将遍历序列的操作与序列底层的结构分离。
ListIterator
ListIterator是Iterator的子类型,只能用于List类的访问。ListIterator可以双向移动。它还可以产生相对于迭代器在列表中指向当前位置的前一个元素和后一个元素的索引。并且可以使用set()方法替换它访问过的最后一个元素。同时,还可以调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素。
/** * ListIterator的使用 * @author LiangYu * 2018-01-16 */ public class ListIteratorTest { @Test public void test() throws Exception { List<Integer> numbers = new ArrayList<Integer>( Arrays.asList(1,3,5,7,9,11,13,15,17,19,21,23,25,27,29)); //创建一个ListIterator ListIterator<Integer> listIterator = numbers.listIterator(); //获取元素为17的前一个元素索引和后一个元素索引 while(listIterator.hasNext()){ System.out.print(listIterator.next()+","+listIterator.previousIndex()+","+listIterator.nextIndex()); System.out.println(); } //创建一个ListIterator,并且,从第10个索引开始 listIterator = numbers.listIterator(10); //反向遍历 while(listIterator.hasPrevious()){ System.out.print(listIterator.previous()+" "); } System.out.println(); //修改元素的值 listIterator = numbers.listIterator(); while(listIterator.hasNext()){ listIterator.set(listIterator.next()-1); } System.out.println(numbers); } }
七、LinkedList
LinkedList也像ArrayList一样,实现了基本的List接口。但是它在执行插入和移除时,比ArrayList高效。
LinkedList还添加了可以使其用作栈、队列、双端队列的方法。这些方法中有些彼此之间只是名字不同或者只存在些许差异。例如:
getFirst()方法和element()方法完全一样,返回列表的第一个元素,而并不移除它,如果List为空,则抛异常。
peek()方法也是获取列表的第一个元素,如果List为空,则返回null。
removeFirst()和remove()完全一样。它们移除并返回列表的头,而在List为空时抛出异常。
poll()方法也是移除并返回头,如果List为空,则返回null。
addFirst()\add()\addLast() 都是在指定位置插入元素。
removeLast() 移除并返回最后一个元素。
/** * LinkedList的使用 * @author LiangYu * 2018-01-16 */ public class LinkedListTest { @Test public void test() throws Exception { LinkedList<String> texts = new LinkedList<String>(Arrays.asList("A","B","C","D","E","F","G","H")); System.out.println(texts); //获取列表的第一个元素 System.out.println("texts.getFirst()--->"+texts.getFirst()); System.out.println("texts.element()--->"+texts.element()); System.out.println("texts.peek()--->"+texts.peek()); //获取并移除列表的第一个元素 System.out.println("texts.remove()--->"+texts.remove()); System.out.println("texts.removeFirst()--->"+texts.removeFirst()); System.out.println("texts.poll()--->"+texts.poll()); //添加元素 texts.offer("I"); System.out.println("texts.offer()--->"+texts); texts.addFirst("J"); System.out.println("texts.addFirst()--->"+texts); texts.add("K"); System.out.println("texts.add()--->"+texts); texts.removeLast(); System.out.println("texts.removeLast()--->"+texts); } }
八、Stack
栈:后进先出。最后一个进栈的元素第一个出来。
LinkedList具备直接实现栈功能的所有方法。不过,为了能够看清楚栈的功能,在这里创建一个Stack类:
/** * 栈 * @author LiangYu * 2018-01-16 */ public class StackTest { @Test public void test() throws Exception { Stack<String> texts = new Stack<String>(); texts.push("A"); texts.push("B"); texts.push("C"); while(!texts.isEmpty()){ System.out.print(texts.pop()); } System.out.println(); } } class Stack<T>{ 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 isEmpty(){ return storage.isEmpty(); } //重载toString @Override public String toString() { return storage.toString(); } }
九、Set
Set不保存重复的元素,如果试图将相同元素的多个实例添加到Set中,那么它会阻止这种重复现象。Set最常被使用的是测试归属性,你可以轻易的询问某个对象是否在Set中,对于Set而言,查找是最重要的操作。
Set和Collection一样的接口,没有任何额外的功能,其实Set就是Collection,只是行为不同。Set是基于对象值来确定归属性。
/** * Set的使用 * @author LiangYu * 2018-01-16 */ public class SetTest { @Test public void test() throws Exception { Random random = new Random(47); Set<Integer> numbers = new HashSet<Integer>(); for(int i = 0 ; i < 10000;i++){ numbers.add(random.nextInt(20)); } System.out.println(numbers); } }
输出的顺序是从0~29。( Java8版本之后,HashSet的底层使用算法已经作了修改。后面会专门写几篇关于utils包下一些类的源码解析,此处不再赘述 )
十、Map
用于将对象映射到其他对象。
例如:检查Random的随机性。(键为随机数,值为该数字出现的次数)
/** * 使用Map容器检查Random * @author LiangYu * 2018-01-16 */ public class RandomTest { @Test public void test() throws Exception { Random random = new Random(47); Map<Integer,Integer> randomMap = new HashMap<Integer,Integer>(); for(int i = 0 ; i < 40000 ; i++){ int r = random.nextInt(20); Integer value = randomMap.get(r); randomMap.put(r, value==null?1:value+1); } System.out.println(randomMap); } }
结论:
1.get(Object key) 通过键获取值
2.put(Object key,Object value) 插入数据
eg:创建一个Map容器,键:宠物名称(String),值:宠物类型(Pet)
/** * 创建一个Map容器,键:宠物名称(String),值:宠物类型(Pet) * @author LiangYu * 2018-01-16 */ public class PetMapTest { @Test public void test() throws Exception { Map<String,Pet> petMap = new HashMap<String,Pet>(); petMap.put("Apache",new Cat()); petMap.put("Cookie", new Dog()); petMap.put("Jerry", new Mouse()); System.out.println(petMap); System.out.println(petMap.get("Cookie")); System.out.println(petMap.containsKey("Apache")); System.out.println(petMap.containsValue(new Mouse())); System.out.println(petMap.containsValue(petMap.get("Jerry"))); } }
Map甚至可以是多维的。 例如:创建一个容器,键为Person对象,值为Pet的List容器,代表一个人拥有多个宠物。
/** * 一个人拥有多个宠物的情况 * @author LiangYu * 2018-01-16 */ public class PersonPetListTest { @Test public void test() throws Exception { Map<Person, List<? extends Pet>> personPetListMap = new HashMap<Person,List<? extends Pet>>(); personPetListMap.put(new Person(0,"KosmoLeung"),Arrays.asList(new Dog(),new Cat())); personPetListMap.put(new Person(1,"AirSu"),Arrays.asList(new BlackMouse(),new BlueCat())); personPetListMap.put(new Person(2,"LindaWu"), Arrays.asList(new BlueMouse(),new WhiteMouse())); System.out.println("People:"+personPetListMap.keySet()); System.out.println("Pets:"+personPetListMap.values()); //遍历Map(同时需要key和value时) for(Map.Entry<Person, List<? extends Pet>> entry : personPetListMap.entrySet()){ System.out.println(entry.getKey()+"---"+entry.getValue()); } //遍历Map(仅需要输出key或value二者之一) for(Person person : personPetListMap.keySet()){ System.out.println(person); } for(List<? extends Pet> petValue : personPetListMap.values()){ System.out.println(petValue); } //遍历Map(可以遍历的同时删除某个元素,用remove方法) Iterator<Map.Entry<Person, List<? extends Pet>>> iterator = personPetListMap.entrySet().iterator(); while(iterator.hasNext()){ Map.Entry<Person,List<? extends Pet>> entry = iterator.next(); System.out.println(entry.getKey()+"---"+entry.getValue()); } } }
十一、Queue
队列:先进先出。从容器的一端放入数据,另一端取出数据。
队列在并发编程中非常重要。(以后写)
LinkedList提供了方法以支持队列的行为。并且它实现了Queue接口。因此LinkedList可以用作Queue的实现。通过将LinkedList向上转型为Queue。
eg:
/** * 队列的使用 * @author LiangYu * 2018-01-16 */ public class QueueTest { void printQueue(Queue queue){ while(queue.peek() != null){ System.out.print(queue.remove()+" "); } System.out.println(); } @Test public void test() throws Exception { Queue<Integer> queue = new LinkedList<Integer>(); Random random = new Random(47); for(int i = 0 ; i < 10 ; i++){ queue.offer(random.nextInt(i+10)); } printQueue(queue); Queue<Character> text = new LinkedList<Character>(); for(char c : "Hello World!".toCharArray()){ text.offer(c); } printQueue(text); } }
结论:
- 添加元素 : offer()
- 获取第一个元素:peek() element()
- 删除元素: poll() remove()
PriorityQueue 优先级队列
对象会在容器内部被排序,默认的 排序 是队列的自然排序,也可以通过提供自己的Comparator来修改这个顺序。
/** * 优先级队列 * @author LiangYu * 2018-01-16 */ public class PriorityQueueTest { @Test public void test() throws Exception { PriorityQueue<Character> pStrings = new PriorityQueue<Character>(); for(char c : "Hello World!".toCharArray()){ pStrings.add(c); } printPriorityQueue(pStrings); } void printPriorityQueue(PriorityQueue<Character> pStrings){ while(pStrings.peek() != null){ System.out.print(pStrings.remove()+" "); } System.out.println(); } }
十二、Collection和Iterator
/** * Collection与Iterator * @author LiangYu * 2018-01-16 */ public class CollectionVSIteratorTest { void display(Iterator<Student> it){ while(it.hasNext()){ Student student = it.next(); System.out.println(student); } System.out.println("*************************************"); } void display(Collection<Student> students){ for(Student student : students){ System.out.println(student); } System.out.println("*************************************"); } @Test public void test() throws Exception { List<Student> students = new ArrayList<Student>(Arrays.asList( new Student(1, "KosmoLeung"), new Student(2, "AirSu"), new Student(3, "LindaWu"), new Student(4, "NancyLee"))); Set<Student> studentSet = new HashSet<Student>(students); Map<String, Student> studentMap = new LinkedHashMap<String,Student>(); String[] ids = "s001,s002,s003,s004".split(","); for(int i = 0 ; i < ids.length ; i++){ studentMap.put(ids[i], students.get(i)); } display(students); display(studentSet); display(students.iterator()); display(studentSet.iterator()); System.out.println(studentMap); System.out.println(studentMap.keySet()); display(studentMap.values()); display(studentMap.values().iterator()); } } class Student{ private int id; private String name; public Student(int id,String name) { this.id = id; this.name = name; } @Override public String toString() { return id+":"+name; } }
结论:
1.Collection接口和Iterator都可以将display方法与底层容器解耦
2.Collection更方便一些,可以使用foreach
Foreach与迭代器
foreach可以应用于任何Collection对象。
因为Java SE5引入了新的被称为Iterable的接口,该接口包含iterator方法,并且Iterable接口被foreach用来在序列中移动。因此,只要你创建了任何实现Iterable的类,都可以将它用于foreach语句。
/** * Iterable的使用 * @author LiangYu * 2018-01-16 */ public class IterableTest { @Test public void test() throws Exception { IterableClass iterableClass = new IterableClass("Hello,World,I,am,Kosmo,Leung,!".split(",")); for(String msg : iterableClass){ System.out.print(msg+" "); } } } class IterableClass implements Iterable<String>{ private String[] word; public IterableClass(String[] word){ this.word = word; } @Override public Iterator<String> iterator() { return new Iterator<String>() { private int index = 0; @Override public boolean hasNext() { return index<word.length; } @Override public String next() { return word[index++]; } }; } }
注意:foreach可以用于数组或其他任何Iterable,但并不意味着数组肯定是Iterable,而自动包装也不会发生
例如:
/** * 数组并不一定是Iterable * @author LiangYu * 2018-01-16 */ public class ArrayIsNotIterableTest { @Test public void test() throws Exception { String[] textArray = {"A","B","C"}; // iterable(textArray); //编译不通过 iterable(Arrays.asList(textArray)); } <T> void iterable(Iterable<T> iterable){ for(T t : iterable){ System.out.print(t+" "); } } }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- [译] 对元素持有弱引用的 Swift 数组
- [译] 对元素持有弱引用的 Swift 数组
- 分类算法解析推测比特币持有者类别与流向
- 区块链RPC攻击借“链”还魂,小蚁币持有者警惕盗币风险
- OC对象的本质 实例对象,类对象,元类对象
- 可迭代对象,迭代器(对象),生成器(对象)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。