Java教程

Java学习笔记之集合(下)

本文主要是介绍Java学习笔记之集合(下),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录
  • 集合输出
    • Iterator迭代器
    • ListIterator
    • Enumeration
    • foreach
  • Map 接口
    • HashMap
    • Hashtable
    • HashMap 与 Hashtable 的区别
    • TreeMap
    • 关于 Map 集合的输出
      • Map.Entry
  • Collections 类
  • 分析 equals、hashCode 与内存泄露
  • 总结

集合输出

我们已经知道了如何使用集合,那么集合如何输出呢?
集合输出,有以下四种方式:

  1. Iteratoor迭代输出(90%)
  2. ListIterator(5%)
  3. Enumeration(1%)
  4. foreach(4%)

在集合输出时,我们应该优先采用Iterator进行输出

Iterator迭代器

Iterator 属于迭代输出,基本的操作原理:是不断的判断是否有下一个元素,有的话,则直接输出。

Iterator为接口,我们进行使用时,需要调用Collection接口的 iterator() 方法获取实例化的Iterator对象

该接口拥用以下方法:

No. 方法名称 描述
1 boolean hasNext() 是否有下一个元素
2 E next() 取出内容
3 void remove() 删除当前内容

注意:

  • 在使用Iterator对象时,我们应该先使用hasNext()进行判断,该方法不仅仅是判断是否存在下一个元素,其还会将Iterator中的操作指针指向下一条元素。

  • 而在Iterator对象未调用hasNext()方法时,其初始指针位于集合的"-1"位置上,如果我们直接使用next()方法取出内容,将会直接抛出异常。如下图所示
    image

  • 示例代码:

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 + "、");
            }
        }
   }
}

image

应将while循环中的代码改为:

while (iter.hasNext()) {// 判断是否有下一个元素
    String str = iter.next(); // 取出当前元素
    if (str.equals("C")) {
    	iter.remove(); // 正确的,调用了Iterator中的删除
    } else {
    	System.out.print(str + "、");
    }
}

控制台输出如下:

image

但是,从实际的开发角度看,元素的删除操作出现的几率是很小的,基本上可以忽略,即:集合中很少有删除元素的操作。

Iterator 接口本身可以完成输出的功能,但是此接口只能进行由前向后的单向输出。如果要想进行双向输出,则必须 使用其子接口 —— ListIterator

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() + "、");
        }
    }
}

控制台输出:

image

但是,此处有一点需要注意的是,如果要想进行由后向前的输出,则首先必须先进行由前向后的输出。

Enumeration

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());
        }
    }
}

控制台输出:

image

需要注意的是,在大部分的情况下,此接口都不再使用了,但是对于一些古老的类库,本身依然要使用此接口进行操作,所以此接口我们也需要掌握。

foreach

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) ;
        }
    }
}

控制台输出:

image

在遍历集合时,其内部实现自动选择效率最高的方式,即 Iterator迭代

在使用 foreach 输出的时候一定要注意的是,里面的操作泛型要指定具体的类型,这样在输出的时候才会更加有针对性

Map 接口

Collection中,每次我们都只能操作一个对象,如果我们需要操作一对对象时,那我们就必须使用Map时,类似于以下这种情况:

  • 张三 ----> 123456
  • 李四 ----> 123456

那么保存以上信息的时候使用 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 本身是一个接口,所以一般会使用以下的几个子类:HashMapTreeMapHashtable

HashMap

HashMapMap的子类,是属于无序存放的

  • 代码示例:向集合中增加内容
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);
    }
}

控制台输出:

image

以上的操作是 Map 接口在开发中最基本的操作过程,根据指定的 key 找到内容,如果没有找到,则返回 null,找到 了则返回具体的内容。

  • 代码示例:得到全部的 key 或 value
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() + "、");
        }
    }
}

控制台输出:

image

  • 代码示例:循环输出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));
        }
    }
}

控制台输出:

image

Hashtable

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);
        }
    }
}

控制台输出:

image

操作时基本上与HashMap并无区别,都是以Map为操作标准,但是

Hashtable 中是不能向集合中插入 null 值的。

HashMap 与 Hashtable 的区别

  1. Hashtable是JDK1.0时推出,比较旧,HashMap是JDK1.2推出的,是新的操作类
  2. Hashtable是线程安全的,性能较低,HashMap是线程不安全的,性能较高
  3. Hashtable不允许设置,否则将出现空指向异常,HashMap允许设置为 null

TreeMap

TreeMap 子类是允许 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));
        }
    }
}

控制台输出:

image

此时的结果已经排序成功了,但是从一般的开发角度来看,在使用 Map 接口的时候并不关心其是否排序,所以此类只需要知道其特点即可。

关于 Map 集合的输出

Map接口没有提供实例Iterator接口的方法,不能直接使用Iterator进行输出

如果我们需要使用 Iterator 进行输出的话,步骤如下:

  1. 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
  2. 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
  3. 之后使用 Iterator 接口进行迭代输出,每一次的迭代都可以取得一个 Map.Entry 的实例
  4. 通过 Map.Entry 进行 key 和 value 的分离

Map.Entry

Map.Entry 本身是一个接口。此接口是定义在 Map 接口内部的,是 Map 的内部接口。此内部接口使用 static 进行定义, 所以此接口将成为外部接口。

实际上来讲,对于每一个存放到 Map 集合中的 key 和 value 都是将其变为了 Map.Entry ,并且将 Map.Entry 保存在了 Map 集合之中。

image

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());
        }
    }
}

控制台输出:

image

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());
        }
    }
}

控制台输出:

image

Collections 类

Collections 是一个操作集合的工具类

  • 代码示例:返回空的 List 集合
public class CollectionsDemo01 {
    public static void main(String[] args) {
        List<String> all = Collections.emptyList() ;// 空的集合
        all.add("A") ;
    }
}

控制台输出:

image

使用 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);
    }
}

控制台输出:

image

但是,从实际考虑,使用此类操作并不是很方便,最好的做法就是使用各个接口的直接操作的方法完成。

分析 equals、hashCode 与内存泄露

  • equals 的作用:比较两个对象的地址值是否相等

equals()方法在 object 类中定义如下:

public boolean equals(Object obj) {
	return (this == obj);
}

但是我们必需清楚,当 String 、Math、还有 Integer、Double。。。。等这些封装类在使用 equals()方法时,已经覆盖了 object 类的 equals()方法,不再是地址的比较而是内容的比较。

我们还应该注意,Java 语言对 equals()的要求如下,这些要求是必须遵循的:

  1. 对称性:如果 x.equals(y)返回是“true”,那么 y.equals(x)也应该返回是“true”。

  2. 反射性:x.equals(x)必须返回是“true”。

  3. 类推性:如果 x.equals(y)返回是“true”,而且 y.equals(z)返回是“true”,那么 z.equals(x)也应该返回是“true”。

  4. 还有一致性:如果 x.equals(y)返回是“true”,只要 x 和 y 内容一直不变,不管你重复 x.equals(y)多少次,返回都是 “true”。

  5. 任何情况下,x.equals(null),永远返回是“false”;x.equals(和 x 不同类型的对象)永远返回是“false”。

    以上这五点是重写 equals()方法时,必须遵守的准则,如果违反会出现意想不到的结果,请大家一定要遵守。

  • hashcode() 方法,在 object 类中定义如下:
public native int hashCode();

说明是一个本地方法,它的实现是根据本地机器相关的。当然我们可以在自己写的类中覆盖 hashcode()方法,比如 String、 Integer、Double。。。。等等这些类都是覆盖了 hashcode()方法的。

java.lnag.Object 中对 hashCode 的约定(很重要):

  1. 在一个应用程序执行期间,如果一个对象的 equals 方法做比较所用到的信息没有被修改的话,则对该对象调用 hashCode 方法多次,它必须始终如一地返回同一个整数。
  2. 如果两个对象根据 equals(Object o)方法是相等的,则调用这两个对象中任一对象的 hashCode 方法必须产生相同的整 数结果。
  3. 如果两个对象根据 equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的 hashCode 方法,不要求产生 不同的整数结果。但如果能不同,则可能提高散列表的性能。
  • 在 java 的集合中,判断两个对象是否相等的规则

    1. 判断两个对象的 hashCode 是否相等
      • 如果不相等,认为两个对象也不相等,完毕
      • 如果相等,转入 2 (这一点只是为了提高存储效率而要求的,其实理论上没有也可以,但如果没有,实际使用时效率会大大降低,所以我们这里将其做为必需的。)
    2. 判断两个对象用 equals 运算是否相等
      • 如果不相等,认为两个对象也不相等
      • 如果相等,认为两个对象相等(equals()是判断两个对象是否相等的关键)

提示贴: 当一个对象被存进 HashSet 集合后,就不能修改这个对象中的那些参与计算的哈希值的字段了,否则,对象被修改后的哈 希值与最初存储进 HashSet 集合中时的哈希值就不同了,在这种情况下,即使在 contains 方法使用该对象的当前引用作为 的参数去 HashSet 集合中检索对象,也将返回找不到对象的结果,这也会导致无法从 HashSet 集合中删除当前对象,从而 造成内存泄露。

总结

  1. 类集就是一个动态的对象数组,可以向集合中加入任意多的内容。
  2. List 接口中是允许有重复元素的,Set 接口中是不允许有重复元素。
  3. 所有的重复元素依靠 hashCode 和 equals 进行区分
  4. List 接口的常用子类:ArrayListVector
  5. Set 接口的常用子类:HashSetTreeSet
  6. TreeSet 是可以排序,一个类的对象依靠 Comparable 接口排序
  7. Map 接口中允许存放一对内容,key → value
  8. Map 接口的子类:HashMapHashtableTreeMap
  9. Map 使用 Iterator 输出的详细步骤
这篇关于Java学习笔记之集合(下)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!