java集合 collection & map

集合框架体系

Java 集合可分为 Collection 和 Map 两大体系

Collection 接口:用于存储一个一个的数据,也称单列数据集合

  • List 子接口:用来存储有序的、可以重复的数据(主要用来替换数组,”动态”数组),实现类,ArrayListLinkedListVector

Set 子接口:用来存储无序的、不可重复的数据(类似于高中讲的 “集合”)

  • 实现类:HashSetLinkedHashSetTreeSet

Map 接口:用于存储具有映射关系 key-value 对的集合

  • 实现类:HashMapLinkedHashMapTreeMapHashtableProperties

Collection 接口及方法

JDK 不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set 和 List)去实现

Collection 接口是 List 和 Set 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 集合

方法 描述
add(E obj) 添加元素对象到当前集合中
addAll(Collection other) 添加 other 集合中的所有元素对象到当前集合中
int size() 获取集合中实际存储的元素个数
Boolean isEmpty() 判断当前集合是否为空
boolean contains(Object obj) 判断当前集合中是否存在一个与 obj 对象 equals 返回 true 的元素
boolean containsAll(Collection coll) 判断 coll 集合中的元素是否在当前集合中都存在。 即 coll 集合是否是当前集合的 “子集”
boolean equals(Object obj) 判断当 前集合与 obj 是否相等
void clear() 清空集合元素
boolean remove(Object obj) 从当前集合中删除第一个找到的与 obj 对象 equals 返回 true 的元素
boolean removeAll(Collection coll) 从当前集合中删除所有与 coll 集合中相同的元素
boolean retainAll(Collection coll) 从当前集合中删除两个集合中不同的元素,即当前集合中仅保留两个集合的交集
Object[] toArray() 返回包含当前集合中所有元素的数组
hashCode() 获取集合对象的哈希值
iterator(): 返回迭代器对象,用于 集合遍历

举例

@Test
public void TestCollection(){
  //ArrayList 是 Collection 的子接口 List 的实现类之一
  Collection list = new ArrayList();
  // add()
  list.add("aa");
  list.add("bb");
  list.add("cc");
  // [aa, bb, cc]
  System.out.println(list);

  Collection list1 = new ArrayList();
  list1.add("ff");
  // addAll
  list1.addAll(list);
  // [ff, aa, bb, cc]
  System.out.println(list1);

  // int size() 4
  System.out.println(list1.size());

  // Boolean isEmpty()
  Collection list2 = new ArrayList();
  // arrayList2 为空 true
  System.out.println(list2.isEmpty());

  // boolean contains(Object obj)
  // list2 包含 aa true
  System.out.println(list1.contains("aa"));

  // boolean  containsAll(Collection coll)
  // list1 都包含 list 的元素
  System.out.println(list1.containsAll(list));
}

Iterator (迭代器)接口

用于遍历集合中的所有元素

Collection 接口继承了 java.lang.Iterable 接口,该接口有一个 iterator()方法,那么所有实现了 Collection 接口的集合类都有一个 iterator()方法,用以返回一个实现了 Iterator 接口的对象

Iterator 接口的常用方法:

  • public E next(): 返回迭代的下一个元素
  • public boolean hasNext(): 如果仍有元素可以迭代,则返回 true

举例

@Test
public void IteratorTest(){
  ArrayList list = new ArrayList();
  list.add(11);
  list.add(22);
  list.add(33);
  list.add(44);
  list.add(55);
  list.add(66);
  // //获取迭代器对象
  Iterator iterator = list.iterator();
  while (iterator.hasNext()){ //判断是否还有元素可迭代
    // 取出下一个元素
    System.out.println(iterator.next());
  }
}

foreach 循环

foreach 循环(也称增强 for 循环)是 JDK5.0 中定义的一个高级 for 循环,专门用来遍历数组和集合

格式:

for(元素的类型 局部变量: Collection 集合或数组 ){}

// 举例
List<Integer> nums = Arrays.asList(1, 2, 3);
for(Integer num : nums){
  // 1 2 3
  System.out.print(num);
}

List 接口

List 除了从 Collection 集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法

方法 描述
void add(int index, Object ele) 在 index 位置插入 ele 元素
boolean addAll(int index, Collection eles) 从 index 位置开始将 eles 中的所有 元素添加进来
Object get(int index) 获取指定 index 位置的元素
List subList(int fromIndex, int toIndex) 返回从 fromIndex 到 toIndex 位置的子集合
int indexOf(Object obj) 返回 obj 在集合中首次出现的位置
int lastIndexOf(Object obj) 返回 obj 在当前集合中末次出现的位置
Object remove(int index) 移除指定 index 位置的元素,并返回此元素
Object set(int index, Object ele) 设置指定 index 位置的元素为 ele

举例

@Test
public void testList(){
  ArrayList list = new ArrayList();
  list.add(0);
  list.add(1);
  list.add(2);

  // 向索引为1 的位置添加
  list.add(1,6);
  // [0, 6, 1, 2]
  System.out.println(list);
  // 删除索引为1的元素
  list.remove(1);
  // [0, 1, 2]
  System.out.println(list);

  // 获取索引为2的元素
  System.out.println(list.get(2));
  // 把索引为0的元素替换10
  list.set(0, 10);
  // [10, 1, 2]
  System.out.println(list);

}

ArrayList

  1. ArrayList 是 list 接口的只要实现类
  2. ArrayList 是对象引用的一个 “变长” 数组
  3. Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是 Vector 实 例,即 Arrays.asList(…) 返回值是一个固定长度的 List 集合

LinkedList

对于频繁的插入或删除元素的操作,建议使用 LinkedList 类,效率较高

底层 采用链表(双向链表)结构存储数据

特有方法:

  • void addFirst(Object obj): 元素添加到头部

  • void addLast(Object obj):元素添加到尾部

  • Object getFirst() :获取头部元素

  • Object getLast() :获取尾部元素

  • Object removeFirst() :删除并返回第一个元素

  • Object removeLast():删除并返回最后一个元素

Vector

Vector 是一个古老的集合,JDK1.0 就有了。大多数操作与 ArrayList 相同,区别之处 在于 Vector 是线程安全的

构造方法

// 创建一个默认的向量,默认大小为 10
Vector();
// 创建指定大小的向量
Vector(int size);
// 创建指定大小的向量,并且增量用 incr 指定
// 增量表示向量每次增加的元素数目
Vector(int size,int incr);
// 创建一个包含集合 c 元素的向量
Vector(Collection c);
  

特有方法

  • void addElement(Object obj) : 将指定的组件添加到此向量的末尾,将其大小增加 1
  • void insertElementAt(Object obj,int index) :将指定对象作为此向量中的组件插入到指定的 index 处
  • void setElementAt(Object obj,int index) :将此向量指定 index 处的组件设置为指定的对象
  • void removeElement(Object obj):从此向量中移除变量的第一个(索引最小的)匹配项
  • void removeAllElements():从此向量中移除全部组件,并将其大小设置为零

Set 接口

  • Set 接口相较于 Collection 接口没有提供额外的方法

  • Set 集合不允许包含相同的元素

HashSet

Set 主要实现类是 HashSet

HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存储、查找、删除性 能

特点:1. 不能保证元素的排列顺序 2. HashSet 不是线程安全的 3. 集合元素可以是 null

HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法得到的 哈希值相等,并且两个对象的 equals()方法返回值为 true

对于存放在 Set 容器中的对象,对应的类一定要重写 hashCode()equals(Object obj) 方法,以实现对象相等规则

HashSet 集合中元素的无序性,不等同于随机性。这里的无序性与元素的添加位置有关,具体的存储位置是由元素的 hashCode()调用后返回的 hash 值决定的

LinkeHashSet

LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以添加顺序保存的

LinkedHashSet 插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很 好的性能

TreeSet

TreeSet 是 SortedSet 接口的实现类,TreeSet 可以按照添加的元素的指定的属性的 大小顺序进行遍历

TreeSet 底层使用红黑树结构存储数据

TreeSet 特点:不允许重复、实现排序(自然排序或定制排序)

对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 或 compare(Object o1,Object o2)方法比较返回值,返回值为 0,则认为两个对象相等

map接口

map 是 集合框架用来存储这种映射关系的对象

Map 与 Collection 并列存在。用于保存具有映射关系的数据:key-value

Map 中的 key 和 value 都可以是任何引用类型的数据。但常用 String 类作为 Map 的 key

key-value 特点:

  • Map 中的 key 用 Set 来存放,不允许重复,即同一个 Map 对象所对应的类,须重写 hashCode()和 equals()方法
  • key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value,不同 key 对应的 value 可以重复。value 所在的类要重写 equals()方法
  • key 和 value 构成一个 entry。所有的 entry 彼此之间是无序的、不可重复的

常用方法

方法 描述
Object put(Object key,Object value) 将指定 key-value 添加到(或修改)当前 map 对象中
void putAll(Map m) 将 m 中的所有 key-value 对存放到当前 map 中
Object remove(Object key) 移除指定 key 的 key-value 对,并返回 value
void clear() 清空当前 map 中的所有数据
Object get(Object key) 获取指定 key 对应的 value
boolean containsKey(Object key) 是否包含指定的 key
boolean containsValue(Object value) 是否包含指定的 value
int size() 返回 map 中 key-value 对的个数
boolean isEmpty() 判断当前 map 是否为空
boolean equals(Object obj) 判断当前 map 和参数对象 obj 是否相等
Set keySet() 返回所有 key 构成的 Set 集合
Collection values() 返回所有 value 构成的 Collection 集合
Set entrySet() 返回所有 key-value 对构成的 Set 集合

举例

@Test
public void testMap(){
  // 创建 map 对象
  HashMap map = new HashMap();
  // put 添加元素
  map.put("jay","可爱女人");
  map.put("fantasy","开不了口");
  map.put("叶惠美","晴天");
  // {jay=可爱女人, fantasy=开不了口, 叶惠美=晴天}
  System.out.println(map);

  // Object remove(Object key) 删除指定的 key-value
  map.remove("jay");
  // {fantasy=开不了口, 叶惠美=晴天}
  System.out.println(map);
  // 查询指定 key 对应的 value "开不了口"
  System.out.println(map.get("fantasy"));

  // 获取所有的key

  Set set = map.keySet();
  //  [fantasy, 叶惠美]
  System.out.println(set);

  // 获取所有 values
  Collection values = map.values();
  // [开不了口, 晴天]
  System.out.println(values);

  //所有的映射关系
  Set entrySet = map.entrySet();
  // [fantasy=开不了口, 叶惠美=晴天]
  System.out.println(entrySet);

}

HashMap

特点

  • 是 Map 接口使用频率最高的实现类
  • 线程不安全的。允许添加 null 键和 null 值
  • 存储数据采用的哈希表结构,底层使用一维数组+单向链表+红黑树进行 key-value 数据的存储。与 HashSet 一样,元素的存取顺序不能保证一致
  • 判断两个 key 相等的标准是:两个 key 的 hashCode 值相等,通过 equals() 方法返回 true
  • 判断两个 value 相等的标准是:两个 value 通过 equals() 方法返回 true

练习:WordCount 统计

统计字符串中每个字符出现的次数

@Test
public void testWordCount() {
  String str = "aaaabbbbbcccdddd";
  char[] chars = str.toCharArray();
  HashMap hashMap = new HashMap();

  for (char c : chars) {
    if (!hashMap.containsKey(c)) {
      hashMap.put(c, 1);
    } else {
      hashMap.put(c, (int)(hashMap.get(c)) + 1);
    }
  }
  // {a=4, b=5, c=3, d=4}
  System.out.println(hashMap);
}

LinkedHashMap

特点

LinkedHashMap 是 HashMap 的子类

存储数据采用的哈希表结构+链表结构,在 HashMap 存储结构的基础上,使用了一对 双向链表来记录添加元素的先后顺序,可以保证遍历元素时,与添加的顺序一致

通过哈希表结构可以保证键的唯一、不重复,需要键所在类重写 hashCode()方法、 equals()方法

TreeMap

特点

TreeMap 存储 key-value 对时,需要根据 key-value 对进行排序。TreeMap 可以保 证所有的 key-value 对处于有序状态

TreeSet 底层使用红黑树结构存储数据

TreeMap 的 Key 的排序:

  • 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException

  • 定制排序:创建 TreeMap 时,构造器传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口

TreeMap 判断两个 key 相等的标准:两个 key 通过 compareTo()方法或者 compare() 方法返回 0

Hashtable

特点:

Hashtable 是 Map 接口的古老实现类,JDK1.0 就提供了。不同于 HashMap, Hashtable 是线程安全的

Hashtable 实现原理和 HashMap 相同,功能相同。底层都使用哈希表结构(数组+单 向链表),查询速度快

与 HashMap 一样,Hashtable 也不能保证其中 Key-Value 对的顺序 • Hashtable 判断两个 key 相等、两个 value 相等的标准,与 HashMap 一致

与 HashMap 不同,Hashtable 不允许使用 null 作为 key 或 value

面试题:Hashtable 和 HashMap 的区别

HashMap: 底层是一个哈希表(jdk7:数组+链表;jdk8:数组+链表+红黑树), 是一个线 程不安全的集合,执行效率高

Hashtable: 底层也是一个哈希表(数组+链表), 是一个线程安全的集合,执行效率低 HashMap 集合 :可以存储 null 的键、null 的值

Hashtable 集合 ,不能存储 null 的键、null 的值

Hashtable 和 Vector 集合一样, 在 jdk1.2 版本之后被更先进的集合(HashMap,Arra yList)取代了。所以 HashMap 是 Map 的主要实现类,Hashtable 是 Map 的古老实现类

Hashtable 的子类 Properties(配置文件)依然活跃在历史舞台 Properties 集合是一个唯一和 IO 流相结合的集合

Properties

特点

Properties 类是 Hashtable 的子类,该对象用于处理属性文件

由于属性文件里的 key、value 都是字符串类型,所以 Properties 中要求 key 和 value 都是字符串类型

存取数据时,建议使用 setProperty(String key,String value)方法和 getProperty(String key)方法

Collections 工具类

Arrays,Collections 是一个操作 Set、List 和 Map 等集合的工具类

常用方法

Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操 作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法(均为 static 方法)

方法 描述
reverse(List) 反转 List 中元素的顺序
shuffle(List) 对 List 集合元素进行随机排序
sort(List) 根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator) 根据指定的 Comparator 产生的顺序对 List 集合元素进行 排序
swap(List,int, int) 将指定 list 集合中的 i 处元素和 j 处元素进行交换
Object max(Collection) / Object max(Collection,Comparator) 根据元素 自然 / Comparator 的顺序 ,返回给定集合中的最大元素
Object min(Collection) / Object min(Collection,Comparator) 根据元素 自然 / Comparator 的顺序 ,返回给定集合中的最小元素
int binarySearch(List list,T key) 在 List 集合中查找某个元素的下标, List 的元素必须是 T 或 T 的子类对象,是可比较大小的,集合也事先必须是有序
int binarySearch(List list,T key,Comparator c) 同上,而且按照 c 比较器规则进行排序过的
void copy(List dest,List src) 将 src 中的内容复制到 dest 中
boolean replaceAll(List list,Object oldVal,Object newVal) 使用新值替换 List 对象 的所有旧值
unmodifiableXxx() 返回指定 Xxx 的不可修改的视图
boolean addAll(Collection c,T… elements) 将所有指定元素添加到指定 collection 中
synchronizedXxx() 可使将指定集合包装成 线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

举例:

@Test
public void testCollections(){
  ArrayList<Object> list = new ArrayList<>();
  // public static <T> boolean addAll(Collection<? super T> c,T...elements)
  // 将所有指定元素添加到指定 collection 中。Collection 的集合的元素类型必须>=T 类型
  Collections.addAll(list,"hello","java",1,2,3);
  System.out.println(list); // [hello, java, 1, 2, 3]

  // Object max(Collection)
  List nums = Arrays.asList(5,8,1,2,7);
  Object max = Collections.max(nums);
  System.out.println(max); // 8

  // reverse
  Collections.reverse(nums);
  System.out.println(nums); // [7, 2, 1, 8, 5]

  // shuffle
  List<String> arrList = Arrays.asList("java", "hello", "world");
  Collections.shuffle(arrList);
  System.out.println(arrList);  // [hello, world, java]

  // void copy(List dest,List src)
  List src = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
  // 将src中的内容复制到dest中  dest的空间大小不能小于src
  List dest = Arrays.asList(new Object[src.size()]);
  Collections.copy(dest,src);
  // [1, 2, 3, 4, 5, 6, 7]
  System.out.println(dest);
}

参考资料

尚硅谷Java基础