java中的集合

出现的原因:

程序总是根据运行时才知道的某些条件去创建新的对象。在此之前,无法知道所需对象的数量甚至确切类型。为了解决这个普
遍的编程问题,需要在任意时刻和任意位置创建任意数量的对象。因此,不能依靠创建命名的引用来持有每一个对象

java.util 库提供了一套相当完整的集合类(collection classes)来解决这个问题,其中基本的类型有 List 、 Set 、 Queue 和 Map。这些类型也被称作容器类(container classes),

基本概念

Java集合类库采用“持有对象”(holding objects)的思想,并将其分为两个不同的概念,表示为类库的基本接口:

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

 

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

ArrayList 已经被向上转型为了 List 

Collection 中的方法(即 add() ),所以使用任何继承自 Collection 的类的对象都可以正常工作。但是 ArrayList 是最基本的序列类型。

add() 方法的名称就表明它是在 Collection 中添加一个新元素。但是,文档中非常详细地叙述到 add() “要确保这个 Collection 包含指定的元素。”这是因为考虑到了 Set 的含义,因为在 Set中,只有当元素不存在时才会添加元素。在使用 ArrayList ,或任何其他类型的 List 时,add() 总是表示“把它放进去”,因为 List 不关心是否存在重复元素。

添加元素组

Arrays.asList() 方法接受一个数组或是逗号分隔的元素列表(使用可变参数),并将其转换为 List 对象。

Collections.addAll() 方法接受一个 Collection 对象,以及一个数组或是一个逗号分隔的列表,将其中元素添加到 Collection 中。

所有 Collection 类型都包含的addAll()

注意:Collection.addAll() 方法只能接受另一个 Collection 作为参数,因此它没有 Arrays.asList() 或 Collections.addAll() 灵活。这两个方法都使用可变参数列表。

可以直接使用 Arrays.asList() 的输出作为一个 List 

List<Snow> snow3 = new ArrayList<>();
  List<Snow> snow4 = Arrays.<Snow>asList(new Light(), new Heavy(), new Slush());
// collections/AsListInference.java
import java.util.*;

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());
    //- snow1.add(new Heavy()); // Exception

    List<Snow> snow2 = Arrays.asList(
      new Light(), new Heavy());
    //- snow2.add(new Slush()); // Exception

    List<Snow> snow3 = new ArrayList<>();
    Collections.addAll(snow3,
      new Light(), new Heavy(), new Powder());
    snow3.add(new Crusty());

    // Hint with explicit type argument specification:
    List<Snow> snow4 = Arrays.<Snow>asList(
       new Light(), new Heavy(), new Slush());
    //- snow4.add(new Powder()); // Exception
  }
}

 

打印集合:

必须使用 Arrays.toString() 来生成数组的可打印形式

package com.company.collection25;

import java.util.*;

public class CollectionAll {
    static Collection
    fill(Collection<String> collection){
        collection.add("AA");
        collection.add("CC");
        collection.add("BB");
        collection.add("CC");
        return collection;
    }

    static Map
    fill(Map <String,String> map){
        map.put("AA","11");
        map.put("CC","22");
        map.put("BB","33");
        map.put("CC","44");
        return map;
    }

    public static void main(String[] args) {
//        List集合,按照顺序保存元素
        System.out.println(fill(new ArrayList<>()));//
        System.out.println(fill(new LinkedList<>()));
//        Set集合,仅保留不同的元素
        System.out.println(fill(new HashSet<>()));
        System.out.println(fill(new TreeSet<>()));//按照升序进行排列
        System.out.println(fill(new LinkedHashSet<>()));
//        Map集合
        System.out.println(fill(new HashMap<>()));//检索是最快的
        System.out.println(fill(new TreeMap<>()));
        System.out.println(fill(new LinkedHashMap<>()));
    }

}

结果:

[AA, CC, BB, CC]
[AA, CC, BB, CC]
[AA, CC, BB]
[AA, BB, CC]
[AA, CC, BB]
{AA=11, CC=44, BB=33}
{AA=11, BB=33, CC=44}
{AA=11, CC=44, BB=33}

默认的打印行为,使用集合提供的 toString() 方法即可生成可读性很好的结果。 Collection 打印出的内容用方括号括住,每个元素由逗号分隔。 Map 则由大括号括住,每个键和值用等号连接(键在左侧,值在右侧)

 

列表List

List 接口在 Collection 的基础上添加了许多方法,允许在 List 的中间插入和删除元素

有两种类型的 List :

基本的 ArrayList ,擅长随机访问元素,但在 List 中间插入和删除元素时速度较慢。
LinkedList ,它通过代价较低的在 List 中间进行的插入和删除操作,提供了优化的顺序访问。 LinkedList 
对于随机访问来说相对较慢,但它具有比 ArrayList 更大的特征集。

 与数组不同, List 可以在创建后添加或删除元素,并自行调整大小。这正是它的重要价值:一种可修改的序列

使用 contains() 方法确定对象是否在列表中
pets.containsAll(sub)
pets.contains(s)

remove() 方法。删除元素
copy.remove(2);
 copy.removeAll(sub); 

可以使用 indexOf() 在 List 中找到该对象所在位置的下标号
pets.indexOf(cymric)

equals() 方法(根类 Object 的一个方法 查看是否相等

subList() 方法可以轻松地创建切片
pets.subList(1, 4)

retainAll() 方法实际上是一个“集合交集”操作
copy.retainAll(sub);


removeAll() 方法也是基于 equals() 方法运行的。 顾名思义,它会从 List 中删除在参数 List 中的所有元素。


 copy.addAll(2, sub);  指定位置插入列表


isEmpty()
clear()

toArray() 方法将任意的 Collection 转换为数组。这是一个重载方法,其无参版本返回一个 Object 数组,
但是如果将目标类型的数组传递给这个重载版本,那么它会生成一个指定类型的数组

 

迭代器Iterators

保存事物是集合最基本的工作。对于 List , add() 是插入元素的一种方式, get() 是获取元素的一种方式。

概念:

迭代器(也是一种设计模式)的概念实现了这种抽象,迭代器通常被称为轻量级对象(lightweight object):创建它的代价小

迭代器是一个对象,它在一个序列中移动并选择该序列中的每个对象,而客户端程序员不知道或不关心该序列的底层结构

约束条件:Iterator 只能单向移动

使用 iterator() 方法要求集合返回一个 Iterator。 Iterator 将准备好返回序列中的第一个元素。
使用 next() 方法获得序列中的下一个元素。
使用 hasNext() 方法检查序列中是否还有元素。
使用 remove() 方法将迭代器最近返回的那个元素删除。
    List<Pet> pets = Pets.list(12);
    Iterator<Pet> it = pets.iterator();
     while(it.hasNext()) {
      Pet p = it.next();
      System.out.print(p.id() + ":" + p + " ");
    }
 public static void display(Iterable<Pet> ip) {
    Iterator<Pet> it = ip.iterator();
    while(it.hasNext()) {
      Pet p = it.next();
      System.out.print(p.id() + ":" + p + " ");
    }
    System.out.println();
  }

 

ListIterator

ListIterator 是一个更强大的 Iterator 子类型,它只能由各种 List 类生成。 Iterator 只能向前移动,而 ListIterator 可以双向移动。它可以生成迭代器在列表中指向位置的后一个和前一个元素的索引,并且可以使用 set() 方法替换它访问过的最近一个元素

可以通过调用 listIterator() 方法来生成指向 List 开头处的 ListIterator ,还可以通过调用 listIterator(n)
创建一个一开始就指向列表索引号为 n 的元素处的 ListIterator

 

 

链表LinkedList

LinkedList 也像 ArrayList 一样实现了基本的 List 接口,但它在 List 中间执行插入和删除操作时比 ArrayList 更高效。然而,它在随机访问操作效率方面却要逊色一些。

getFirst() 和 element() 是相同的,它们都返回列表的头部(第一个元素)而并不删除它,如果 List 为空,
  则抛出 NoSuchElementException 异常。 peek() 方法与这两个方法只是稍有差异,它在列表为空时返回 null 。 removeFirst() 和 remove() 也是相同的,它们删除并返回列表的头部元素,并在列表为空时
  抛出 NoSuchElementException 异常。 poll() 稍有差异,它在列表为空时返回
null 。 addFirst() 在列表的开头插入一个元素。 offer() 与 add() 和 addLast() 相同。 它们都在列表的尾部(末尾)添加一个元素。 removeLast() 删除并返回列表的最后一个元素。

堆栈Stack

堆栈是“后进先出”(LIFO)集合。它有时被称为叠加栈(pushdown stack),因为最后“压入”(push)栈的元素,
第一个被“弹出”(pop)栈。经常用来类比栈的事物是带有弹簧支架的自助餐厅托盘。最后装入的托盘总是最先拿出来使用的。
package com.company.collection25.stacks;

import java.util.Stack;

public class StackTest1 {
    public static void main(String[] args) {
        Stack<String> stack = new Stack<>();
        for (String s:"I AM A BETTER MAN".split(" ")){
//            在stack将s进栈
            stack.push(s);
        }
        while (!stack.isEmpty()){
//            pop返回并删除stack顶部的元素。
            System.out.println(stack.pop());
        }
    }
}
/***
 * MAN
 * BETTER
 * A
 * AM
 * I
 */

注意:

Java 1.0 中附带了一个 Stack 类,结果设计得很糟糕(为了向后兼容,我们永远坚持 Java 中的旧设计错误)。Java 6 添加了 ArrayDeque ,其中包含直接实现堆栈功能的方法

// onjava/Stack.java
// A Stack class built with an ArrayDeque
package onjava;
import java.util.Deque;
import java.util.ArrayDeque;

public class Stack<T> {
  private Deque<T> storage = new ArrayDeque<>();
  public void push(T v) { storage.push(v); }
  public T peek() { return storage.peek(); }
  public T pop() { return storage.pop(); }
  public boolean isEmpty() { return storage.isEmpty(); }
  @Override
  public String toString() {
    return storage.toString();
  }
}

类名称后面的 告诉编译器这是一个参数化类型,而其中的类型参数 T 会在使用类时被实际类型替换。基本上,这个类是在声明“我们在定义一个可以持有 T 类型对象的 Stack 。” Stack 是使用 ArrayDeque 实现的,而 ArrayDeque 也被告知它将持有 T 类型对象。注意, push() 接受类型为 T 的对象,而 peek() 和 pop() 返回类型为 T 的对象。 peek() 方法将返回栈顶元素,但并不将其从栈顶删除,而 pop() 删除并返回顶部元素。

 

 

集合Set

Set 不保存重复的元素。 如果试图将相同对象的多个实例添加到 Set 中,那么它会阻止这种重复行为。 Set 最常见的用途是测试归属性,可以很轻松地询问某个对象是否在一个 Set 中。因此,查找通常是 Set 最重要的操作,因此通常会选择 HashSet 实现,该实现针对快速查找进行了优化。

Set 具有与 Collection 相同的接口,实际上, Set 就是一个 Collection ,只是行为不同

package com.company.collection25.set;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class SetOfInteger {
    public static void main(String[] args) {
        Set<Integer> integers = new HashSet<>();
        Random random = new Random(47);
        for (int i = 0; i < 30;i++){
            integers.add(random.nextInt(100));
        }
        System.out.println(integers);
        System.out.println(integers.size());
    }
}

由 HashSet 维护的顺序与 TreeSet 或 LinkedHashSet 不同,因为它们的实现具有不同的元素存储方式。 TreeSet 将元素存储在红-黑树数据结构中,而 HashSet 使用散列函数。 LinkedHashSet 因为查询速度的原因也使用了散列,但是看起来使用了链表来维护元素的插入顺序。看起来散列算法好像已经改变了,现在 Integer 按顺序排序

使用 contains() 测试成员归属性

映射Map

 

posted @ 2020-12-09 08:06  小丑quan  阅读(96)  评论(0编辑  收藏  举报