Java集合超详细整理——Onjava8

java.util包中提供了一套相当完整的集合类,来储存复杂的对象,且只能存放对象,即时它保存的是基本数据类型,也会将它自动转换成Integer包装类型之后再存入集合中。集合中存放的都是对象的引用而非对象那个本身。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GigTF83N-1638944147174)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211207184802969.png)]

数组和集合的区别:

  1. 数组长度固定,集合长度可变
  2. 数组可以存值也可以存对象的引用,但集合只能存对象的引用。

集合中的泛型和类型安全

import java.util.*;

class Apple {
  private static long counter;
  private final long id = counter++;
  public long id() { return id; }
}

class Orange {}

public class ApplesAndOrangesWithoutGenerics {
  public static void main(String[] args) {
    ArrayList apples = new ArrayList();
    for(int i = 0; i < 3; i++)
      apples.add(new Apple());
    apples.add(new Orange());
    for(Object apple : apples) {
      ((Apple) apple).id();

    }
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xc7a7euM-1638944147175)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211207194513524.png)]

在这个例子中,Apple和Orange都被添加到了集合当中,两个截然不同的类为什么可以被添加到同一个集合中,答案就是他们的父类都是Object类而List保存的是Object类对象,所以不论是编译期还是执行期都可以将Orange放入List中,不会出现任何问题。但是当你调用get()方法想要取出你认为是Apple的对象时必须将Object向下强制转换成Apple。但是在将Orange对象转换到Apple对象时会报ClassCastExcption转换异常。

为了防止这种错误我们采用了泛型apples 定义的右侧,可以看到 new ArrayList<>() 。这有时被称为“菱形语法”(diamond syntax)。在 Java 7 之前,必须要在两端都进行类型声明,如下所示:

ArrayList<Apple> apples = new ArrayList<Apple>();

随着类型变得越来越复杂,这种重复产生的代码非常混乱且难以阅读。程序员发现所有类型信息都可以从左侧获得,因此,编译器没有理由强迫右侧再重复这些。虽然类型推断(type inference)只是个很小的请求,Java 语言团队仍然欣然接受并进行了改进。有了 ArrayList 声明中的类型指定,编译器会阻止将 Orange 放入 apples ,因此,这会成为一个编译期错误而不是运行时错误。使用泛型,从 List 中获取元素不需要强制类型转换。因为 List 知道它持有什么类型,因此当调用 get() 时,它会替你执行转型。因此,使用泛型,你不仅知道编译器将检查放入集合的对象类型,而且在使用集合中的对象时也可以获得更清晰的语法。通过使用泛型我们可以更加规范插入集合的元素类型。

集合的基本组成

集合包含两大基本概念,表现为两大接口:CollectionMap

  1. **Collection:**一个独立元素的序列,这些元素都服从一条或多条规则。List 必须以插入的顺序保存元素, Set 不能包含重复元素, Queue 按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
  2. Map: 一组成对的“键值对”对象,允许使用键来查找值。 ArrayList 使用数字来查找对象,因此在某种意义上讲,它是将数字和对象关联在一起。 map 允许我们使用一个对象来查找另一个对象,它也被称作关联数组(associative array),因为它将对象和其它对象关联在一起;或者称作字典(dictionary),因为可以使用一个键对象来查找值对象,就像在字典中使用单词查找定义一样。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CgJ8SgEk-1638944147176)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211207195916298.png)]

Iterators迭代器

迭代器(也是一种设计模式)的概念实现了这种抽象。迭代器是一个对象,它在一个序列中移动并选择该序列中的每个对象,而客户端程序员不知道或不关心该序列的底层结构。另外,迭代器通常被称为轻量级对象(lightweight object):创建它的代价小。因此,经常可以看到一些对迭代器有些奇怪的约束。例如,Java 的 Iterator 只能单向移动。这个 Iterator 只能用来:

  1. 使用 iterator() 方法要求集合返回一个 IteratorIterator 将准备好返回序列中的第一个元素。
  2. 使用 next() 方法获得序列中的下一个元素。
  3. 使用 hasNext() 方法检查序列中是否还有元素。
  4. 使用 remove() 方法将迭代器最近返回的那个元素删除。

List集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tAMVxZrr-1638944147176)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211208110442575.png)]

List承诺将元素保存在特定的序列中,基本的List的实现类有两种,ArrayList和LinkedList;ArrayList和LinkedList都同时实现了List接口并且继承了AbstractList类。

  • ArrayList擅长随机访问元素,但在进行中间插入和删除元素时较慢。
  • LinkedList再进行中间插入和删除操作时代价更小,并且提供了优化的顺序访问;但它在随机访问中速度是较慢的。

Set集合

Set 不保存重复的元素。 如果试图将相同对象的多个实例添加到 Set 中,那么它会阻止这种重复行为。 Set 最常见的用途是测试归属性,可以很轻松地询问某个对象是否在一个 Set 中。因此,查找通常是 Set 最重要的操作,因此通常会选择 HashSet 实现,该实现针对快速查找进行了优化。Set 具有与 Collection 相同的接口,因此没有任何额外的功能,不像前面两种不同类型的 List 那样。实际上, Set 就是一个 Collection ,只是行为不同。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U5aQdQYT-1638944147177)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211208112612926.png)]

Set接口的子类有HashSetTreeSetLinkedHashSetHashSet使用了散列表,TreeSet使用了红黑树的数据结构来储存,而LinkedHashSet则是在使用散列表的基础上使用了链表来维护元素的插入顺序。

映射Map(双列集合)

元素包含两个值(key,value)即键值对, key不允许重复,value可以重复, key与value是一一对应的。元素无序;

Map不是继承的Collection,Map 是一组成对的“键值对”对象,允许使用键来查找值。map 允许我们使用一个对象来查找另一个对象,它也被称作关联数组(associative array),因为它将对象和其它对象关联在一起;或者称作字典(dictionary),因为可以使用一个键对象来查找值对象,就像在字典中使用单词查找定义一样。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-khkvEBlL-1638944147177)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211208115538636.png)]

Map的四种遍历方式

  1. 普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
  for (String key : map.keySet()) {
   System.out.println("key= "+ key + " and value= " + map.get(key));
  }
  1. 通过Map.entrySet使用iterator遍历key和value
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
  Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
  while (it.hasNext()) {
   Map.Entry<String, String> entry = it.next();
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
  1. 通过Map.entrySet使用iterator遍历key和value
 System.out.println("通过Map.entrySet遍历key和value");
  for (Map.Entry<String, String> entry : map.entrySet()) {
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
  1. 通过Map.values()遍历所有的value,但不能遍历key
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
  for (String v : map.values()) {
   System.out.println("value= " + v);
  }
 }

感谢您的阅读,如果本篇文章对您有帮助,欢迎点赞,关注,您的阅读是我莫大的鼓励!


版权声明:本文为missingtheway原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。