疯狂Java讲义_Chapter08集合

1.Java集合概述

  • Java集合的功能:
  1. 用于存储数量不等的对象;
  2. 实现常用的数据结构;
  3. 保存具有关联性的数据(map);
  • 集合类可以解决数组长度不可变导致的数据存储问题;
  • 所有的集合类都位于java.util包下;
  • 集合类中只能保存对象,值类型进入集合会自动装箱;
  • Collection和Map是集合类的根接口;
  • Set和List接口是Collection的两个子接口,分别代表无序集合和有序集合;Set中的元素不能重复;
  • Map由key-value这种键值对数据组成,key是唯一的;
  • 访问List可以根据索引,访问map可以根据key,但是访问set只能根据值本身访问;
  • 最常用的实现类:HashSet, TreeSet, ArrayList, ArraryDeque, LinkedList, HashMap, TreeMap;

2.Java11增强的Collection和Iterator接口

1.概述

  • Collection接口中的方法:
  1. boolean add(Object obj):向容器中添加数据;
  2. boolean addAll(Collection c):把集合c中所有元素添加到指定的集合中;
  3. void clear():清除集合里的所有元素,将集合的长度变为0;
  4. boolean contains(Object obj):
  5. boolean containsAll(Collection c):
  6. boolean isEmpty();
  7. Iterator iterator():返回一个iterator对象,用于遍历集合;
  8. boolean remove(Object obj):删除集合中指定的元素obj,只删除第一个符合条件的元素;
  9. boolean removeAll(Collection c)
  10. int size()
  11. Object[] toArray():将集合转换为数组;
  • 用ArrayList实现Collection接口;

import java.util.*;

public class Collection
{
    public static void main(String[] args)
    {
        var c = new ArrayList();
        // 添加元素
        c.add("元素1");   //不启用泛型的情况下都是Object
        // 支持自动装箱
        c.add(6);
        System.out.println("容器c的大小为:" + c.size());
        // 移除元素,参数是元素的索引
        c.remove(1);
        System.out.println("容器c的大小为:" + c.size());
        System.out.println("c集合是否包含\"元素1\"字符串?"
                + c.contains("元素1"));
        c.add("一本书");
        System.out.println("c中的元素" + c);

        var books = new HashSet();
        books.add("一本书");
        books.add("Java");
        System.out.println("c中是否包含book中的元素"
                + c.containsAll(books));
        // 从c中删除book中的元素
        c.removeAll(books);
        System.out.println("c中的元素" + c);
        // 清空c
        c.clear();
        System.out.println("c中的元素" + c);
        //让books中只含有c中的元素
        books.retainAll(c);
        System.out.println("books中有" + books);

    }
}
  •  

2.使用lambda表达式遍历集合

  • 把lambda表达式就想成是:参数->{方法体}的一种形式;


import java.util.*;

public class CollectionEach
{
	public static void main(String[] args)
	{
		var books = new HashSet();
		books.add("������Java EE��ҵӦ��ʵս");
		books.add("���Java����");
		books.add("���Android����");
		// Collection的forEach方法遍历容器
		books.forEach(obj -> System.out.println("��������Ԫ�أ�" + obj));
	}
}

3.使用iterator遍历集合元素

  • Iterator接口定义了4个方法:

  1. boolean hasNext():如果集合里含有元素,则返回true;

  2. Object next():返回集合里的下一个元素;

  3. void remove():删除集合里上一次next()方法返回的元素;

  4. void forEachRemaining(Cosumer action):这是java8为Iterator新增的默认方法,可以用lambda表达式遍历集合元素


import java.util.*;

public class IteratorTest
{
	public static void main(String[] args)
	{
		// 新建一个hashSet;
		var books = new HashSet();
		books.add("a");
		books.add("b");
		books.add("n");
		// 在容器基础上建立一个迭代器;
		var it = books.iterator();
		while (it.hasNext())
		{
			// 从容器中拿出元素,并转换为String类型;
			var book = (String) it.next();
			System.out.println(book);
			if (book.equals("a"))
			{
				// 从集合中删除next出来的元素
				it.remove();
			}
			// 对元素进行赋值
			book = "f";   // ��
		}
		System.out.println(books);
	}
}
  •  想要用Iterator就必须要有一个容器;

4.使用lambda表达式遍历Iterator(299)

5.使用foreach循环遍历集合元素

  • 程序示例:


import java.util.*;

public class ForeachTest
{
	public static void main(String[] args)
	{
		var books = new HashSet();
		books.add(new String("a"));
		books.add(new String("b"));
		books.add(new String("c"));
		for (var obj : books)
		{
			var book = (String) obj;
			System.out.println(book);
			if (book.equals("a"))
			{
				books.remove(book);   
			}
		}
		System.out.println(books);
	}
}

7.使用Predicate操作集合(300)

6.使用Stream操作集合(301)

3.Set集合

1.HashSet(300)

  • HashSet采用Hash算法来存储集合中的元素;
  • HashSet的特点:
  1. 不能保证元素的排列顺序,顺序可能与添加顺序不同;
  2. HashSet不是线程同步的;
  3. 集合元素的值可以是null;
  • 当向HashSet集合中存入一个元素时,HashSet对调用该对象的hashCode方法计算对象的哈希值,根据哈希值决定该对象的存储位置;
  • 如果需要把某个类的对象放入HashSet中,要重写这个类的HashCode和equal方法,保证在equal返回true的同时,HashCode返回true;否则会导致重复存储同样数据的问题,与Set的规范冲突;

//想要把不同对象存入HashSet,必须将HashCode和equal都进行重写
import java.util.*;

class A
{
	public boolean equals(Object obj)
	{
		return true;
	}
}
class B
{
	public int hashCode()
	{
		return 1;
	}
}
class C
{
	public int hashCode()
	{
		return 2;
	}
	public boolean equals(Object obj)
	{
		return true;
	}
}

public class HashSetTest
{
	public static void main(String[] args)
	{
		var books = new HashSet();
		books.add(new A()); //没有重写hashcode,保留两个
		books.add(new A());
		books.add(new B()); //没有重写equal方法,保留两个
		books.add(new B());
		books.add(new C()); //equal和hashcode都进行了重写,Set只保留一个
		books.add(new C());
		System.out.println(books);
	}
}
  •  HashSet中每个能存储元素的“槽位”通常称为“bucket”;
  • 重写HashCode方法的基本规则
  1. 同一个对象调用HashCode方法,返回同样的值;
  2. 当两个对象调用equal方法返回true,HashCode方法应该返回相同的值;
  3. 对象中用作equals方法比较标准的实例变量,都应该用于计算HashCode值;

2.TreeSet

  • TreeSet是SortedSet接口的实现类,TreeSet可以保证集合元素处于排序状态;
  • TreeSet的常规方法(308);
import java.util.*;

public class TreeSetTest
{
	public static void main(String[] args)
	{
		var nums = new TreeSet();
		nums.add(5);
		nums.add(2);
		nums.add(10);
		nums.add(-9);
		System.out.println(nums);
		// 输出第一个元素
		System.out.println(nums.first()); // 输出-9
		System.out.println(nums.last());  // 输出10
		// 返回小于4的子集
		System.out.println(nums.headSet(4)); 
		// 返回大于5的子集
		System.out.println(nums.tailSet(5)); 
		// 返回大于-3,小于4的子集
		System.out.println(nums.subSet(-3, 4)); 
	}
}
  •  TreeSet支持自然排序和定制排序两种策略(309)

3.LinkedHashSet

  • LinkedHashSet是HashSet的子类,LinkedHashSet使用链表维护元素的存储次序,当遍历LinkedHashSet时,链表将以添加顺序进行访问;

import java.util.*;

public class LinkedHashSetTest
{
	public static void main(String[] args)
	{
		var books = new LinkedHashSet();
		books.add("1号");
		books.add("2号");
		System.out.println(books);
		books.remove("1号");
		books.add("3号");
		System.out.println(books);
	}
}

 

4.EnumSet

  • EnumSet是专门为枚举类设计的集合类;

  • EnumSet集合不允许加入null;

  • EnumSet集合的创建方法(315);


import java.util.*;

enum Season
{
	SPRING,SUMMER,FALL,WINTER
}
public class EnumSetTest
{
	public static void main(String[] args)
	{
		// 创建EnumSet,集合元素是枚举类的全部值
		var es1 = EnumSet.allOf(Season.class);
		System.out.println(es1); // 输出[SPRING, SUMMER, FALL, WINTER]
		// 创建一个空的EnumSet,指定其集合元素是Season类的枚举值
		var es2 = EnumSet.noneOf(Season.class);
		System.out.println(es2); // 输出[]
		// 添加枚举值
		es2.add(Season.WINTER);
		es2.add(Season.SPRING);
		System.out.println(es2);
		//创建一个带有部分元素的EnumSet
		var es3 = EnumSet.of(Season.SUMMER, Season.WINTER);
		System.out.println(es3); 
		var es4 = EnumSet.range(Season.SUMMER, Season.WINTER);
		System.out.println(es4); 
		
		var es5 = EnumSet.complementOf(es4);
		System.out.println(es5); // ���[SPRING]
	}
}

5.Set的性能分析

  • HashSet优于TreeSet;

  • LinkedHashSet在查询上优于HashSet;插入删除上HashSet优于LinkedHashSet;

  • 所有的Set都是线程不安全的;

 

4.List集合

1.改进的List接口和ListIterator接口

  • List集合有序,且允许数据重复;
  • List提供的根据索引来操作数据的方法(316)
import java.util.*;

public class ListTest
{
	public static void main(String[] args)
	{
		var books = new ArrayList();
		// List的常用方法
		books.add("a");
		books.add("b");
		books.add("n");
		System.out.println(books);
		// 插入新的字符串
		books.add(1, new String("ab"));
        //遍历元素
		for (var i = 0; i < books.size(); i++)
		{
			System.out.println(books.get(i));
		}
		// 移除元素
		books.remove(2);
		System.out.println(books);
		// 输出字符串所在位置
		System.out.println(books.indexOf(new String("a"))); // ��
		//重置某个位置的元素
		books.set(1, "vnbg");
		System.out.println(books);
		// [1,2]之间的全部字符
		System.out.println(books.subList(1, 2));
	}
}
  • List提供了sort()和replaceAll()方法来对List进行排序:

import java.util.*;

public class ListTest3
{
	public static void main(String[] args)
	{
		var books = new ArrayList();
		// 添加元素
		books.add("7");
		books.add("8");
		books.add("16");
		books.add("11");
		// 使用目标类型为Comparator的Lambda表达式对List集合排序
		books.sort((o1, o2) -> ((String) o1).length() - ((String) o2).length());
		System.out.println(books);
		// 使用目标类型为UnaryOprator的Lambda表达式来替换集合中的所有元素
		books.replaceAll(ele -> ((String) ele).length());
		System.out.println(books); //输出[7, 8, 11, 16]
	}
}
  • List提供了listIterator()方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口:
  1. boolean hasPrevious():返回该迭代器关联的集合是否还有上一个元素;
  2. Object previous():返回该迭代器的上一个元素;
  3. void add(Object obj):在指定位置插入一个元素
  • ListIterator的用法;

import java.util.*;

public class ListIteratorTest
{
	public static void main(String[] args)
	{
		String[] books = {
		        "abc","cbd"
		};
		var bookList = new ArrayList();
		for (var i = 0; i < books.length; i++)
		{
			bookList.add(books[i]);
		}
		var lit = bookList.listIterator();
		// 正向遍历
		while (lit.hasNext())
		{
			System.out.println(lit.next());
			lit.add("---分隔符-----");
		}
		System.out.println("=====下面开始反向迭代=====");
		// 从后向前遍历
		while (lit.hasPrevious())
		{
			System.out.println(lit.previous());
		}
	}
}

 

2.ArrayList和Vector实现类

  • ArrayList和Vector类都是基于数组实现的List类,他们内部封装了一个动态数据Object[],数组会随着数据的增加而扩大;
  • Vector有很多缺点,尽量避免使用Vector;
  • 可以用initialCapacity(num)方法指定ArrayList的初始长度;也可以用ensureCapacity(int minCapacity)来一次性增加数组大小;

5.Queue集合

  • Queue集合的作用是模拟队列;队列是先进先出类型数据结构;
  • Queue中定义的方法:
  1. void add(Object o):将元素加入队列尾部;
  2. Object element():获取队列头部元素,不删除;
  3. boolean offer(Object o):将指定元素加入此队列的尾部;当使用容量有限的队列时,这个方法效率更好;
  4. Object peek():获取队列头部的元素,但是不删除元素;
  5. Object poll():获取头部元素并删除该元素;
  6. Object remove():获取头部元素并删除该元素;

1.PriorityQueue实现类

  • PriorityQueue保存队列元素的顺序是按大小排列的;

import java.util.*;

public class PriorityQueueTest
{
	public static void main(String[] args)
	{
		var pq = new PriorityQueue();
		// 下面代码依次向pq中加入四个元素
		pq.offer(6);
		pq.offer(-3);
		pq.offer(20);
		pq.offer(18);
		// 输出pq队列,并不是按元素的加入顺序排列
		System.out.println(pq); // 输出[-3, 6, 20, 18]
		// 访问队列第一个元素,其实就是队列中最小的元素:-3
		System.out.println(pq.poll());
	}
}
  • PriorityQueue不允许添加null元素; 

2.Deque接口和ArrayDeque实现类

  • Deque接口是Queue接口的子接口,代表一个双端队列,Deque接口的方法(322);
  • Deque可以实现栈的功能,并且建议采用Deque作为实现栈的方式,因为Stack太老了;

import java.util.*;

public class ArrayDequeStack
{
	public static void main(String[] args)
	{
		var stack = new ArrayDeque();
		// 依次将三个元素push入"栈"
		stack.push("疯狂Java讲义");
		stack.push("轻量级Java EE企业应用实战");
		stack.push("疯狂Android讲义");
		// 输出:[疯狂Android讲义, 轻量级Java EE企业应用实战, 疯狂Java讲义]
		System.out.println(stack);
		// 访问第一个元素,但并不将其pop出"栈",输出:疯狂Android讲义
		System.out.println(stack.peek());
		// 依然输出:[疯狂Android讲义, 疯狂Java讲义, 轻量级Java EE企业应用实战]
		System.out.println(stack);
		// pop出第一个元素,输出:疯狂Android讲义
		System.out.println(stack.pop());
		// 输出:[轻量级Java EE企业应用实战, 疯狂Java讲义]
		System.out.println(stack);
	}
}
  • Deque作为队列:

import java.util.*;

public class ArrayDequeQueue
{
	public static void main(String[] args)
	{
		var queue = new ArrayDeque();
		// 依次将三个元素加入队列
		queue.offer("疯狂Java讲义");
		queue.offer("轻量级Java EE企业应用实战");
		queue.offer("疯狂Android讲义");
		// 输出:[疯狂Java讲义, 轻量级Java EE企业应用实战, 疯狂Android讲义]
		System.out.println(queue);
		// 访问队列头部的元素,但并不将其poll出队列"栈",输出:疯狂Java讲义
		System.out.println(queue.peek());
		// 依然输出:[疯狂Java讲义, 轻量级Java EE企业应用实战, 疯狂Android讲义]
		System.out.println(queue);
		// poll出第一个元素,输出:疯狂Java讲义
		System.out.println(queue.poll());
		// 输出:[轻量级Java EE企业应用实战, 疯狂Android讲义]
		System.out.println(queue);
	}
}

 

3.性能分析

  1. 强调搜索功能,就用ArrayList;强调插入和删除功能,就用linkedList;
  2. 遍历ArrayList,建议用get方法;遍历LinkedList,建议用Iterator迭代器;
  3. 如果多个线程需要同时访问List,可以用Collections将集合封装成线程安全的集合;

6.增强的Map集合

1.Java8为Map新增的方法

  • Map用于保存具有映射关系的数据(key-value),Map的key是唯一的,value可以重复;
  • Map也被称为是字典,Map中定义的方法如下:
  1. void clear():删除该Map对象中的所有key-value对;
  2. boolean containsKey(Object key):查询Map中是否包含对应的key;
  3. boolean containsValue(Object value):查询Map中是否包含对应的value;
  4. Set entrySet():返回Map中包含的key-value对所组成的Set集合;
  5. Object get(Object key):返回指定key所对应的value值,如果不存在,返回null;
  6. boolean isEmpty():判断Map是否为空;
  7. set KeySet():返回该Map中所有key组成的Set集合;
  8. Object putAll(Map m):将指定的Map复制到本Map中;
  9. Object remove(Object key):删除指定key所对应的key-value对,返回被删除key所关联的value;
  10. boolean remove(Object key, Object value):删除指定key,value对应的key-value对,返回布尔值;
  11. int size():返回该Map对应的key-value对数;
  • Map内包含一个内部类Entry,该类封装了一个key-value对:
  1. Object getKey():返回Entry中的key值;
  2. Object getValue():返回Entry中的value值;
  3. Object setValue(V value):设置该Entry中包含的value值,并返回新设置的value值;

import java.util.*;

public class MapTest
{
	public static void main(String[] args)
	{
		var map = new HashMap();
		// 成对放入多个key-value对
		map.put("疯狂Java讲义", 109);
		map.put("疯狂iOS讲义", 10);
		map.put("疯狂Ajax讲义", 79);
		// 多次放入的key-value对中value可以重复
		map.put("轻量级Java EE企业应用实战", 99);
		// 放入重复的key时,新的value会覆盖原有的value
		// 如果新的value覆盖了原有的value,该方法返回被覆盖的value
		System.out.println(map.put("疯狂iOS讲义", 99)); // 输出10
		System.out.println(map); // 输出的Map集合包含4个key-value对
		// 判断是否包含指定key
		System.out.println("是否包含值为 疯狂iOS讲义 的key:"
			+ map.containsKey("疯狂iOS讲义")); // 输出true
		// 判断是否包含指定value
		System.out.println("是否包含值为 99 的value:"
			+ map.containsValue(99)); // 输出true
		// 获取Map集合的所有key组成的集合,通过遍历key来实现遍历所有key-value对
		for (var key : map.keySet())
		{
			// map.get(key)方法获取指定key对应的value
			System.out.println(key + "-->" + map.get(key));
		}
		map.remove("疯狂Ajax讲义"); // 根据key来删除key-value对。
		System.out.println(map); // 输出结果不再包含 疯狂Ajax讲义=79 的key-value对
	}
}
  •  Java8为Map提供了一些新的方法,见327页;

import java.util.*;

public class MapTest2
{
	public static void main(String[] args)
	{
		var map = new HashMap();
		// 成对放入多个key-value对
		map.put("疯狂Java讲义", 109);
		map.put("疯狂iOS讲义", 99);
		map.put("疯狂Ajax讲义", 79);
		// 尝试替换key为"疯狂XML讲义"的value,由于原Map中没有对应的key,
		// 因此对Map没有改变,不会添加新的key-value对
		map.replace("疯狂XML讲义", 66);
		System.out.println(map);
		// 使用原value与参数计算出来的结果覆盖原有的value
		map.merge("疯狂iOS讲义", 10,
			(oldVal, param) -> (Integer) oldVal + (Integer) param);
		System.out.println(map); // "疯狂iOS讲义"的value增大了10
		// 当key为"Java"对应的value为null(或不存在时),使用计算的结果作为新value
		map.computeIfAbsent("Java", key -> ((String) key).length());
		System.out.println(map); // map中添加了 Java=4 这组key-value对
		// 当key为"Java"对应的value存在时,使用计算的结果作为新value
		map.computeIfPresent("Java",
			(key, value) -> (Integer) value * (Integer) value);
		System.out.println(map); // map中 Java=4 变成 Java=16
	}
}

2.改进的HashMap和Hashtable实现类(329)

3.LinkedHashMap实现类(331)

4.使用Properties读写属性文件

  • Properties类是Hashtable的子类,该对象在处理属性文件非常方便,它可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件中;

  • Properties相当于一个key, value都是String类型的Map;

  • Properties可以把key-value对以XML文件的形式保存起来,也可以从XML文件中加载key-value对;

  • Properties中的方法(332)


import java.util.*;
import java.io.*;

public class PropertiesTest
{
	public static void main(String[] args)
		throws Exception
	{
		var props = new Properties();
		// 向Properties中增加属性
		props.setProperty("username", "yeeku");
		props.setProperty("password", "123456");
		// 将Properties中的key-value对保存到a.ini文件中
		props.store(new FileOutputStream("a.ini"),
			"comment line");   // ①
		// 新建一个Properties对象
		var props2 = new Properties();
		// 向Properties中增加属性
		props2.setProperty("gender", "male");
		// 将a.ini文件中的key-value对追加到props2中
		props2.load(new FileInputStream("a.ini"));   // ②
		System.out.println(props2);
	}
}

5.SortedMap接口和TreeMap实现类(333)

6.WeakHashMap实现类

  • 对于一般的HashMap,只要HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收;但是WeakHashMap中的key所引用的对象没有被其他引用的话,JVM会回收这些对象,WeakHashMap也会自动删除这些键值对;

import java.util.*;
/**
 * Description:
 * 网站: <a href="http://www.crazyit.org">疯狂Java联盟</a><br>
 * Copyright (C), 2001-2020, Yeeku.H.Lee<br>
 * This program is protected by copyright laws.<br>
 * Program Name:<br>
 * Date:<br>
 * @author Yeeku.H.Lee kongyeeku@163.com
 * @version 5.0
 */
public class WeakHashMapTest
{
	public static void main(String[] args)
	{
		var whm = new WeakHashMap();
		// 将WeakHashMap中添加三个key-value对,
		// 三个key都是匿名字符串对象(没有其他引用)
		whm.put(new String("语文"), new String("良好"));
		whm.put(new String("数学"), new String("及格"));
		whm.put(new String("英文"), new String("中等"));
		//将 WeakHashMap中添加一个key-value对,
		// 该key是一个系统缓存的字符串对象。
		whm.put("java", new String("中等"));    // ①
		// 输出whm对象,将看到4个key-value对。
		System.out.println(whm);
		// 通知系统立即进行垃圾回收
		System.gc();
		System.runFinalization();
		// 通常情况下,将只看到一个key-value对。
		System.out.println(whm);
	}
}

7.IdentityHashMap实现类(336)

8.Map的性能分析

  •  HashMap比Hashtable快;
  • TreeMap很慢,但是TreeMap的优点是有序;
  • HashMap是最优选;

 

7.HashSet和HasMap的性能选项(339)

8.操作集合的工具类:Collections

1.排序操作

  • Collections类提供了对Set, List, Map等集合的操作方法;
  • Collections提供的排序方法:
  1. void reverse(List list):反转指定list集合中元素的顺序;
  2. void shuffle(List list):对List集合进行随机排序(洗牌);
  3. void sort(List list):根据元素的自然顺序对指定的List集合的元素按升序排序;
  4. void sort(List list, Comparator c):根据Comparator产生的顺序对List进行排序;
  5. void swap(List list, int i, int j):将指定集合中的i元素和j元素进行替换;
  6. void rotate(List list, int distance);

2.查找/替换操作(342)

3.同步控制

  • Collections提供了synchronizedXxx()方法,可以将指定的集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题;

import java.util.*;

public class SynchronizedTest
{
	public static void main(String[] args)
	{
		// 下面程序创建了四个线程安全的集合对象
		var c = Collections
			.synchronizedCollection(new ArrayList());
		var list = Collections.synchronizedList(new ArrayList());
		var s = Collections.synchronizedSet(new HashSet());
		var m = Collections.synchronizedMap(new HashMap());
	}
}

4.设置不可变集合(343)

5.Java9新增不可变集合(343)

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值