我们已经知道了如何使用集合,那么集合如何输出呢?
集合输出,有以下四种方式:
Iteratoor
迭代输出(90%)ListIterator
(5%)在集合输出时,我们应该优先采用Iterator
进行输出
Iterator
属于迭代输出,基本的操作原理:是不断的判断是否有下一个元素,有的话,则直接输出。
Iterator
为接口,我们进行使用时,需要调用Collection
接口的 iterator() 方法获取实例化的Iterator
对象
该接口拥用以下方法:
No. | 方法名称 | 描述 |
---|---|---|
1 | boolean hasNext() | 是否有下一个元素 |
2 | E next() | 取出内容 |
3 | void remove() | 删除当前内容 |
注意:
在使用Iterator对象时,我们应该先使用hasNext()进行判断,该方法不仅仅是判断是否存在下一个元素,其还会将Iterator中的操作指针指向下一条元素。
而在Iterator对象未调用hasNext()方法时,其初始指针位于集合的"-1"位置上,如果我们直接使用next()方法取出内容,将会直接抛出异常。如下图所示
示例代码:
public class IteratorDemo01 { public static void main(String[] args) { Collection<String> all = new ArrayList<String>(); all.add("A"); all.add("B"); all.add("C"); all.add("D"); all.add("E"); Iterator<String> iter = all.iterator(); while (iter.hasNext()) {// 判断是否有下一个元素 String str = iter.next(); // 取出当前元素 System.out.print(str + "、"); } } }
控制台输出:
进行迭代输出的时候如果要想删除当前元素,则只能使用 Iterator 接口中的 remove()方法,而不能使用集合中的 remove()方法。否则将出现未知的错误。
public class IteratorDemo02 { public static void main(String[] args) { Collection<String> all = new ArrayList<String>(); all.add("A"); all.add("B"); all.add("C"); all.add("D"); all.add("E"); Iterator<String> iter = all.iterator(); while (iter.hasNext()) {// 判断是否有下一个元素 String str = iter.next(); // 取出当前元素 if (str.equals("C")) { all.remove(str); // 错误的,调用了集合中的删除 } else { System.out.print(str + "、"); } } } }
应将while循环中的代码改为:
while (iter.hasNext()) {// 判断是否有下一个元素 String str = iter.next(); // 取出当前元素 if (str.equals("C")) { iter.remove(); // 正确的,调用了Iterator中的删除 } else { System.out.print(str + "、"); } }
控制台输出如下:
但是,从实际的开发角度看,元素的删除操作出现的几率是很小的,基本上可以忽略,即:集合中很少有删除元素的操作。
Iterator
接口本身可以完成输出的功能,但是此接口只能进行由前向后的单向输出。如果要想进行双向输出,则必须 使用其子接口 —— ListIterator
ListIterator
是可以进行双向输出的迭代接口
其为Iterator
的子接口,在其父接口Iterator
的基础上增加了以下操作方法
No. | 方法名称 | 描述 |
---|---|---|
1 | void add(E e) | 增加元素 |
2 | boolean hasPrevious() | 判断是否有前一个元素 |
3 | E previous() | 取出前一个元素 |
4 | void set(E e) | 修改元素的内容 |
5 | int previousIndex() | 前一个索引位置 |
6 | int nextIndex() | 下一个索引位置 |
如果要想使用 ListIterator
接口,则必须使用 List
接口的 listIterator() 获取其实例化对象
代码示例:
public class ListIteratorDemo { public static void main(String[] args) { List<String> all = new ArrayList<String>(); all.add("A"); all.add("B"); all.add("C"); all.add("D"); all.add("E"); ListIterator<String> iter = all.listIterator(); System.out.print("从前向后输出:"); while (iter.hasNext()) { System.out.print(iter.next() + "、"); } System.out.print("\n从后向前输出:"); while (iter.hasPrevious()) { System.out.print(iter.previous() + "、"); } } }
控制台输出:
但是,此处有一点需要注意的是,如果要想进行由后向前的输出,则首先必须先进行由前向后的输出。
Enumeration
是一个非常古老的输出接口,其也是一个元老级的输出接口,最早的动态数组使用Vector
完成,那么只 要是使用了 Vector
则就必须使用 Enumeration
进行输出。
No. | 方法名称 | 描述 |
---|---|---|
1 | boolean hasMoreElements() | 判断是否有下一个元素 |
2 | E nextElement() | 取出当前元素 |
Enumeration
接口,则必须使用 Vector
接口的 elements() 获取其实例化对象public class EnumerationDemo01 { public static void main(String[] args) { Vector<String> v = new Vector<String>(); v.add("A"); v.add("B"); v.add("C"); Enumeration<String> enu = v.elements(); while (enu.hasMoreElements()) { System.out.println(enu.nextElement()); } } }
控制台输出:
需要注意的是,在大部分的情况下,此接口都不再使用了,但是对于一些古老的类库,本身依然要使用此接口进行操作,所以此接口我们也需要掌握。
foreach 是一种for循环的简化方式,可以用来输出数组的内容,那么也可以输出集合中的内容。
代码示例:
public class ForeachDemo { public static void main(String[] args) { Collection<String> all = new ArrayList<String>(); all.add("A"); all.add("B"); all.add("C"); all.add("D"); all.add("E"); for (String str : all) { System.out.println(str) ; } } }
控制台输出:
在遍历集合时,其内部实现自动选择效率最高的方式,即 Iterator
迭代
在使用 foreach 输出的时候一定要注意的是,里面的操作泛型要指定具体的类型,这样在输出的时候才会更加有针对性
在Collection
中,每次我们都只能操作一个对象,如果我们需要操作一对对象时,那我们就必须使用Map时,类似于以下这种情况:
那么保存以上信息的时候使用 Collection 就不那么方便,所以要使用 Map 接口。里面的所有内容都按照 key→value 的形式保存,也称为键值对。
此接口与 Collection 接口没有任何的关系,是第二大的集合操作接口。此接口常用方法如下:
No. | 方法名称 | 描述 |
---|---|---|
1 | void clear() | 清空 Map 集合中的内容 |
2 | boolean containsKey(Object key) | 判断集合中是否存在指定的 key |
3 | boolean containsValue(Object value) | 判断集合中是否存在指定的 value |
4 | Set> entrySet() | 将 Map 接口变为 Set 集合 |
5 | V get(Object key) | 根据 key 找到其对应的value |
6 | boolean isEmpty() | 判断是否为空 |
7 | Set keySet() | 将全部的 key 变为 Set 集合 |
8 | Collection values() | 将全部的 value 变为 Collection 集合 |
9 | V put(K key,V value) | 向集合中增加内容 |
10 | void putAll(Map m) | 增加一组集合 |
11 | V remove(Object key) | 根据 key 删除内容 |
Map
本身是一个接口,所以一般会使用以下的几个子类:HashMap
、TreeMap
、Hashtable
HashMap
为Map
的子类,是属于无序存放的
public class HashMapDemo01 { public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "张三A"); map.put(1, "张三B"); // 新的内容替换掉旧的内容 map.put(2, "李四"); map.put(3, "王五"); String val = map.get(6); System.out.println(val); } }
控制台输出:
以上的操作是 Map 接口在开发中最基本的操作过程,根据指定的 key 找到内容,如果没有找到,则返回 null,找到 了则返回具体的内容。
public class HashMapDemo02 { public static void main(String[] args) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "张三A"); map.put(2, "李四"); map.put(3, "王五"); Set<Integer> set = map.keySet(); // 得到全部的key Collection<String> value = map.values(); // 得到全部的value Iterator<Integer> iter1 = set.iterator(); Iterator<String> iter2 = value.iterator(); System.out.print("全部的key:"); while (iter1.hasNext()) { System.out.print(iter1.next() + "、"); } System.out.print("\n全部的value:"); while (iter2.hasNext()) { System.out.print(iter2.next() + "、"); } } }
控制台输出:
Map
中的全部内容public class HashMapDemo03 { public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("ZS", "张三"); map.put("LS", "李四"); map.put("WW", "王五"); map.put("ZL", "赵六"); map.put("SQ", "孙七"); Set<String> set = map.keySet(); // 得到全部的key Iterator<String> iter = set.iterator(); while (iter.hasNext()) { String i = iter.next(); // 得到key System.out.println(i + " --:> " + map.get(i)); } } }
控制台输出:
Hashtable
是一个最早的 key→value 的操作类,本身是在 JDK 1.0 的时候推出的。其基本操作与 HashMap
是类似的。
public class HashtableDemo01 { public static void main(String[] args) { Map<String, Integer> numbers = new Hashtable<String, Integer>(); numbers.put("one", 1); numbers.put("two", 2); numbers.put("three", 3); Integer n = numbers.get("two"); if (n != null) { System.out.println("two = " + n); } } }
控制台输出:
操作时基本上与HashMap
并无区别,都是以Map
为操作标准,但是
Hashtable
中是不能向集合中插入 null 值的。
Hashtable
是JDK1.0时推出,比较旧,HashMap
是JDK1.2推出的,是新的操作类Hashtable
是线程安全的,性能较低,HashMap
是线程不安全的,性能较高Hashtable
不允许设置,否则将出现空指向异常,HashMap
允许设置为 nullTreeMap 子类是允许 key 进行排序的操作子类,其本身在操作的时候将按照 key 进行排序,另外,key 中的内容可以为任意的对象,但是要求对象所在的类必须实现 Comparable 接口
public class TreeMapDemo01 { public static void main(String[] args) { Map<String, String> map = new TreeMap<String, String>(); map.put("ZS", "张三"); map.put("LS", "李四"); map.put("WW", "王五"); map.put("ZL", "赵六"); map.put("SQ", "孙七"); Set<String> set = map.keySet(); // 得到全部的key Iterator<String> iter = set.iterator(); while (iter.hasNext()) { String i = iter.next(); // 得到key System.out.println(i + " --:> " + map.get(i)); } } }
控制台输出:
此时的结果已经排序成功了,但是从一般的开发角度来看,在使用 Map 接口的时候并不关心其是否排序,所以此类只需要知道其特点即可。
Map接口没有提供实例Iterator
接口的方法,不能直接使用Iterator
进行输出
如果我们需要使用 Iterator 进行输出的话,步骤如下:
Map.Entry
本身是一个接口。此接口是定义在 Map
接口内部的,是 Map
的内部接口。此内部接口使用 static 进行定义, 所以此接口将成为外部接口。
实际上来讲,对于每一个存放到 Map
集合中的 key 和 value 都是将其变为了 Map.Entry
,并且将 Map.Entry
保存在了 Map 集合之中。
Map.Entry
的常用方法如下:
No. | 方法名称 | 描述 |
---|---|---|
1 | K getKey() | 得到 key |
2 | V getValue() | 得到 value |
public class MapOutDemo01 { public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("ZS", "张三"); map.put("LS", "李四"); map.put("WW", "王五"); map.put("ZL", "赵六"); map.put("SQ", "孙七"); Set<Map.Entry<String, String>> set = map.entrySet();// 变为Set实例 Iterator<Map.Entry<String, String>> iter = set.iterator(); while (iter.hasNext()) { Map.Entry<String, String> me = iter.next(); System.out.println(me.getKey() + " --> " + me.getValue()); } } }
控制台输出:
Map 集合中每一个元素都是 Map.Entry 的实例,只有通过 Map.Entry 才能进行 key 和 value 的分离操作。
除了以上的做法之外,在 JDK 1.5 之后也可以使用 foreach 完成同样的输出,只是这样的操作基本上不使用。
public class MapOutDemo02 { public static void main(String[] args) { Map<String, String> map = new HashMap<String, String>(); map.put("ZS", "张三"); map.put("LS", "李四"); map.put("WW", "王五"); map.put("ZL", "赵六"); map.put("SQ", "孙七"); for (Map.Entry<String, String> me : map.entrySet()) { System.out.println(me.getKey() + " --> " + me.getValue()); } } }
控制台输出:
Collections
是一个操作集合的工具类
public class CollectionsDemo01 { public static void main(String[] args) { List<String> all = Collections.emptyList() ;// 空的集合 all.add("A") ; } }
控制台输出:
使用 Collections
类返回的空的集合对象,本身是不支持任何的修改操作的,因为所有的方法都没实现。
Collections
进行增加元素的操作public class CollectionsDemo02 { public static void main(String[] args) { List<String> all = new ArrayList<String>(); Collections.addAll(all, "A", "B", "C");// 向集合增加元素 System.out.println(all); } }
控制台输出:
但是,从实际考虑,使用此类操作并不是很方便,最好的做法就是使用各个接口的直接操作的方法完成。
equals()方法在 object 类中定义如下:
public boolean equals(Object obj) { return (this == obj); }
但是我们必需清楚,当 String 、Math、还有 Integer、Double。。。。等这些封装类在使用 equals()方法时,已经覆盖了 object 类的 equals()方法,不再是地址的比较而是内容的比较。
我们还应该注意,Java 语言对 equals()的要求如下,这些要求是必须遵循的:
对称性:如果 x.equals(y)返回是“true”,那么 y.equals(x)也应该返回是“true”。
反射性:x.equals(x)必须返回是“true”。
类推性:如果 x.equals(y)返回是“true”,而且 y.equals(z)返回是“true”,那么 z.equals(x)也应该返回是“true”。
还有一致性:如果 x.equals(y)返回是“true”,只要 x 和 y 内容一直不变,不管你重复 x.equals(y)多少次,返回都是 “true”。
任何情况下,x.equals(null),永远返回是“false”;x.equals(和 x 不同类型的对象)永远返回是“false”。
以上这五点是重写 equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。
public native int hashCode();
说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖 hashcode()方法,比如 String、 Integer、Double。。。。等等这些类都是覆盖了 hashcode()方法的。
java.lnag.Object 中对 hashCode 的约定(很重要):
在 java 的集合中,判断两个对象是否相等的规则
提示贴: 当一个对象被存进
HashSet
集合后,就不能修改这个对象中的那些参与计算的哈希值的字段了,否则,对象被修改后的哈 希值与最初存储进HashSet
集合中时的哈希值就不同了,在这种情况下,即使在 contains 方法使用该对象的当前引用作为 的参数去HashSet
集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet
集合中删除当前对象,从而 造成内存泄露。
List
接口中是允许有重复元素的,Set
接口中是不允许有重复元素。ArrayList
、Vector
HashSet
、TreeSet
TreeSet
是可以排序,一个类的对象依靠 Comparable 接口排序Map
接口中允许存放一对内容,key → valueMap
接口的子类:HashMap
、Hashtable
、TreeMap
Map
使用 Iterator
输出的详细步骤