第11章 持有对象
集合类: List/Set/Queue/Map
11.1 泛型和类型安全的容器
public class Apple {
public void name(){
System.out.println("Apple");
}
}
public class Orange {
}
// 不使用泛型
public class Test {
@SuppressWarnings("unchecked") // 抑制警告信息
public static void main(String[] args) {
// 希望创建Apple容器
ArrayList arrayList = new ArrayList();
arrayList.add(new Apple());
arrayList.add(new Apple());
// 可以添加任意类型
arrayList.add(new Orange());
for (Object o : arrayList) {
// 获得对象时,必须转型
((Apple) o).name();
}
}
}
// 使用泛型
public class Test {
@SuppressWarnings("unchecked") // 抑制警告信息
public static void main(String[] args) {
ArrayList<Apple> arrayList = new ArrayList<>();
arrayList.add(new Apple());
arrayList.add(new Apple());
// 不可以添加除指定类型
// arrayList.add(new Orange());
// 无需转型
for (Apple apple : arrayList) {
apple.name();
}
}
}
11.2 基本概念
Collection 一个独立元素的序列。List按照插入顺序保存,Set不能有重复元素,Queue按照排队规则确认元素的顺序。
Map 一组成对的“键值对”对象,允许使用键来查找值,被称为映射表或者关联数组。
public class Test {
public static void main(String[] args) {
Collection<Integer> c = new ArrayList<>();
for (int i = 0; i < 10; i++) {
// 注意:List总会添加,Set不存在时添加,存在时忽略
c.add(i);
}
c.forEach(i -> System.out.println(i));
}
}
11.3 添加一组元素
基本使用
public class Test {
public static void main(String[] args) {
// Arrays.asList()将数组或者可变参数转为List
Integer[] integers = new Integer[]{1,2,3};
List<Integer> asList1 = Arrays.asList(integers);
List<Integer> asList2 = Arrays.asList(1, 2, 3);
// 根据已有集合创建新集合
Collection<Integer> c = new ArrayList<>(asList1);
// Collection.addAll()方法,添加集合
c.addAll(asList2);
// Collections.addAll()向集合中添加数组或者可变参数
Collections.addAll(c,integers);
Collections.addAll(c,1,2,3);
}
}
asList()问题
class Snow{}
class A extends Snow{}
class B extends Snow{}
class Power extends Snow{}
class e extends Power{}
class f extends Power{}
public class Test {
public static void main(String[] args) {
List<Snow> snows1 = Arrays.asList(new A(), new e(), new f());
// 因为目标中最近的公约数是Power,该方法不会创建List<Snow>
List<Power> snows2 = Arrays.asList(new e(), new f());
// 使用显示类型参数说明解决这一问题
List<Snow> snows = Arrays.<Snow>asList(new e(), new f());
}
}
11.4 容器的打印
public class Test {
public void fill(Collection<String> c){
Collections.addAll(c,"a","b","c","a","b");
System.out.println(c);
}
public void fill(Map<String,String> map){
map.put("name","ding");
map.put("age","12");
map.put("high","175");
System.out.println(map);
}
public static void main(String[] args) {
Test test = new Test();
test.fill(new ArrayList<>());
test.fill(new LinkedList<>());
test.fill(new HashSet<>());
test.fill(new TreeSet<>());
test.fill(new LinkedHashSet<>());
test.fill(new HashMap<>());
test.fill(new TreeMap<>());
test.fill(new LinkedHashMap<>());
}
}
/*
[a, b, c, a, b]
[a, b, c, a, b]
[a, b, c]
[a, b, c]
[a, b, c]
{high=175, name=ding, age=12}
{age=12, high=175, name=ding}
{name=ding, age=12, high=175}
*/
11.5 List
ArrayList 随机访问快,中间插入删除慢
LinkedList 中间插入删除快,随机访问慢
public class Test {
public static void print(Object o) {
System.out.println(o);
}
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "a", "b", "h"));
print("1 " + list);
// 末尾添加元素
list.add("e");
// 指定位置插入元素,其他顺序后移
list.add(1, "f");
print("2 " + list);
// 是否包含该元素
boolean e = list.contains("e");
print("3 " + e);
// 删除指定的元素
boolean f = list.remove("f");
// 删除指定位置的元素
list.remove(4);
print("4 " + f);
// 获取指定位置的元素
String s = list.get(0);
print("5 " + s);
// 获取指定元素的第一个索引
int a = list.indexOf("a");
print("6 " + a);
print(list);
// 截取子集合,左闭右开,注意,子集合是老集合的映像,两者的改变会相互影响
List<String> subList = list.subList(0, 3);
print("7 " + subList);
print(subList);
// 建立和原有集合不相关的新集合
ArrayList<String> strings = new ArrayList<>(subList);
strings.remove("a");
// 是否包含指定集合的所有元素
boolean all = list.containsAll(subList);
print("8 " + all);
// 升序排序集合
Collections.sort(list);
print("9 " + list);
// 打乱集合
Collections.shuffle(list);
print("10 " + list);
// 取得两个集合的交集
list.retainAll(strings);
print("11 " + list);
// 移除指定集合中的所有元素
list.removeAll(strings);
print("12 " + list);
list.add(0, "p");
// 替换指定位置的元素
list.set(0, "o");
print("13 " + list);
// 判断集合是否为空
boolean empty = list.isEmpty();
// 获得集合的元素数
int num = list.size();
print("14 " + empty);
// 集合转数组
Object[] objects = list.toArray();
String[] strings1 = list.toArray(new String[0]);
print("15 " + strings1[0]);
}
}
/*
1 [a, b, c, a, b, h]
2 [a, f, b, c, a, b, h, e]
3 true
4 true
5 a
6 0
[a, b, c, a, h, e]
7 [a, b, c]
[a, b, c]
8 true
9 [a, a, b, c, e, h]
10 [e, c, h, a, b, a]
11 [c, b]
12 []
13 [o]
14 false
15 o
*/
11.6 迭代器
.基本使用
public class Test {
public static void print(Object o) {
System.out.println(o);
}
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c", "a", "b"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
print(iterator.next());
}
iterator = list.iterator();
for (int i = 0; i < 3; i++) {
iterator.next();
// 调用remove方法前必须调用next()方法
iterator.remove();
}
print(list);
}
}
解耦操作
获得序列中的对象与容器分离
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c", "a", "b");
ArrayList<String> arrayList = new ArrayList<>(list);
LinkedList<String> linkedList = new LinkedList<>(list);
HashSet<String> hashSet = new HashSet<>(list);
TreeSet<String> treeSet = new TreeSet<>(list);
display(arrayList.iterator());
display(linkedList.iterator());
display(hashSet.iterator());
display(treeSet.iterator());
}
public static void display(Iterator<String> it){
while (it.hasNext()){
System.out.println(it.next());
}
}
ListIterator
public static void main(String[] args) {
List<String> aslist = Arrays.asList("a", "b", "c");
List<String> list = new ArrayList<>(aslist);
ListIterator<String> it = list.listIterator();
while (it.hasNext()){
// 当前元素 下一个元素索引 前一个元素索引
System.out.println(it.next()+" "+it.nextIndex()+" "+ it.previousIndex());
/*
a 1 0
b 2 1
c 3 2
*/
// 前一个元素
//it.previous(); // 需要注销,否则死循环
}
while (it.hasPrevious()){
System.out.println(it.previous());
/*
c
b
a
*/
}
// 指定迭代器开始的位置
it = list.listIterator(1);
while (it.hasNext()){
System.out.println(it.next());
/*
b
c
*/
}
}
11.7 LinkedList
public static void main(String[] args) {
List<String> aslist = Arrays.asList("a", "b", "c");
//List<String> aslist = Arrays.asList();
LinkedList<String> linkedList = new LinkedList<>(aslist);
// 获取第一个元素
String first = linkedList.getFirst(); // 集合为空抛异常NoSuchElementException
String element = linkedList.element(); // 集合为空抛异常NoSuchElementException
String peek = linkedList.peek(); // 获得null
// 删除第一个元素
String remove = linkedList.remove(); // 集合为空抛异常NoSuchElementException
String removeFirst = linkedList.removeFirst(); // 集合为空抛异常NoSuchElementException
String poll = linkedList.poll(); // 获得null
// 在头部添加元素
linkedList.addFirst("f"); // 无返回
// 在尾部添加元素
boolean e = linkedList.add("e"); // 返回是否成功
linkedList.addLast("g");
// 移除最后一个元素
String removeLast = linkedList.removeLast();
}
11.8 Stack(栈)
class Stack<T>{
private LinkedList<T> linkedList = new LinkedList<>();
/**
* 添加元素
* @param t
* @return
*/
public void push(T t){
linkedList.addFirst(t);
}
/**
* 获得元素
* @return
*/
public T peek(){
return linkedList.getFirst();
}
/**
* 移除元素
* @return
*/
public T pop(){
return linkedList.removeFirst();
}
/**
* 判断是否为空
* @return
*/
public boolean isEmpty(){
return linkedList.isEmpty();
}
public String toString(){
return linkedList.toString();
}
}
public static void main(String[] args) {
List<String> aslist = Arrays.asList("a", "b", "c");
Stack<String> stack = new Stack<>();
for (String s : aslist) {
stack.push(s);
}
System.out.println(stack);
while (!stack.isEmpty()){
System.out.println(stack.peek());
stack.pop();
}
}
注意: 如果只需要栈的行为,java.util包中的Stack是不合适的,它不该继承集合类,继承导致它有了其他方法。
11.9 Set
Set最长用的场景是判断元素是否在该集合下。
public static void main(String[] args) {
Random random = new Random();
Set<Integer> set1 = new HashSet<>();
Set<Integer> set2 = new TreeSet<>();
Set<Integer> set3 = new LinkedHashSet<>();
for (int i = 0; i < 10000; i++) {
// 随机排序,会出现按顺序排序的假象,因为hashcode相同
set1.add(random.nextInt(20));
// 按顺序排序
set2.add(random.nextInt(20));
// 按插入顺序排序
set3.add(random.nextInt(20));
}
System.out.println(set1);
System.out.println(set2);
System.out.println(set3);
/*
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[0, 2, 12, 3, 7, 8, 13, 5, 9, 16, 6, 17, 15, 14, 19, 18, 1, 10, 11, 4]
*/
}
基本用法
public static void main(String[] args) {
List<String> asList = Arrays.asList("a", "b", "c");
// 创建
Set<String> set1 = new HashSet<>(asList);
// 是否包含
System.out.println("1 "+set1.contains("a"));
Set<String> set2 = new HashSet<>();
// 批量添加
Collections.addAll(set2,"a","b");
// 是否批量包含
boolean containsAll = set1.containsAll(set2);
System.out.println("2 "+containsAll);
// 批量删除
boolean removeAll = set1.removeAll(set2);
System.out.println("3 "+set2);
}
11.10 Map
验证Random的随机性
public static void main(String[] args) {
Map<Integer,Integer> map = new HashMap<>();
Random random = new Random();
for (int i = 0; i < 1000; i++) {
int key = random.nextInt(20);
Integer v = map.get(key);
map.put(key,v == null?1:++v);
}
System.out.println(map);
}
/*
{0=52, 1=50, 2=48, 3=54, 4=61, 5=44, 6=55, 7=56, 8=57, 9=40, 10=56, 11=52, 12=51, 13=35, 14=50, 15=52, 16=47, 17=38, 18=46, 19=56}
*/
是否包含key和value
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("name","ding");
map.put("age","13");
System.out.println(map.containsKey("name")); // true
System.out.println(map.containsValue("ding")); // true
}
组成更复杂的结构
public static void main(String[] args) {
Map<String,List<Integer>> map = new HashMap<>();
map.put("骰子",Arrays.asList(1,2,3,4,5,6));
map.put("扑克牌",Arrays.asList(1,2,3,4));
System.out.println(map.keySet()); // key的Set
System.out.println(map.values()); // values的Collection
// 遍历map的实体
for (Map.Entry<String, List<Integer>> stringListEntry : map.entrySet()) {
System.out.println(stringListEntry.getKey());
// 遍历实体中的List
for (Integer i : stringListEntry.getValue()) {
System.out.println(i);
}
}
}
11.11 Queue队列
基本使用
public static void main(String[] args) {
Queue<Character> queue = new LinkedList<>();
for (char c : "Hello World".toCharArray()) {
// 将一个元素插入队尾
queue.offer(c);
}
re(queue);
}
public static void re(Queue queue){
// 返回队头,队列为空null
// queue.element() != null NoSuchElementException
while (queue.peek() != null){
// 移除返回队头
System.out.println(queue.remove()); // NoSuchElementException
//queue.poll() 为空为null
}
}
PriorityQueue(优先级队列)
插入时会根据优先级顺序排列,优先级高的排在前面,默认从小到大。
public static void main(String[] args) {
PriorityQueue priorityQueue = new PriorityQueue();
Random random = new Random();
for (int i = 0; i < 10; i++) {
int an = random.nextInt(20);
priorityQueue.offer(an);
}
re(priorityQueue); // 1 2 2 3 7 10 15 17 19 19
String s = "abcegdfjsdsa";
List<String> stringList = Arrays.asList(s.split(""));
priorityQueue = new PriorityQueue(stringList);
// 可以接收相同的元素
re(priorityQueue); // a a b c d d e f g j s s
// 反转优先级
priorityQueue = new PriorityQueue(stringList.size(),Collections.reverseOrder());
priorityQueue.addAll(stringList);
re(priorityQueue); // s s j g f e d d c b a a
// 去重
Set<Character> set = new HashSet<>();
for (char c : s.toCharArray()) {
set.add(c);
}
priorityQueue = new PriorityQueue(set);
re(priorityQueue); // a b c d e f g j s
}
11.12 Collection和Iterator
Collection是所有序列容器的根接口,使用Collection可以创建更通用的代码;Iterator(迭代器)也可以实现相同的功能;在实现Collection接口时,必须实现iterator()方法,因此,在某些情况下,使用迭代器效果会更好一些。
另:Collection接口继承了Iterable接口,实现了Iterable接口的类都可以使用Foreach.
public class InterfaceVSIterator {
/**
* 展示容器中的所有内容
* @param c 集合
*/
public static void disapply(Collection<String> c){
for (String s : c) {
System.out.print(s+" ");
}
System.out.println();
}
/**
* 展示容器中的所有内容
* @param it 迭代器
*/
public static void diapply(Iterator<String> it){
while (it.hasNext()){
System.out.print(it.next() +" ");
}
System.out.println();
}
public static void main(String[] args) {
List<String> list = Arrays.asList("a","b","c");
InterfaceVSIterator.disapply(list);
InterfaceVSIterator.diapply(list.iterator());
}
/*
a b c
a b c
*/
}
// 继承Collection接口
public class CollectionSequence extends AbstractCollection<String> {
private String[] strings = new String[]{"a","b","c"};
@Override
public Iterator<String> iterator() {
Iterator<String> it = new Iterator<String>() {
// 定义计数器
private int index = 0;
@Override
public boolean hasNext() {
return index < strings.length;
}
@Override
public String next() {
// 返回当前计数的元素,并将计数器+1
return strings[index++];
}
};
return it;
}
@Override
public int size() {
return strings.length;
}
public static void main(String[] args) {
CollectionSequence c = new CollectionSequence();
InterfaceVSIterator.disapply(c);
InterfaceVSIterator.diapply(c.iterator());
/*
a b c
a b c
*/
}
}
// 实现迭代器方法
public class StringSequence {
protected String[] strings = new String[]{"a","b","c"};
}
class NoCollectionSequence extends StringSequence{
public Iterator<String> iterator(){
return new Iterator<String>() {
private int index =0;
@Override
public boolean hasNext() {
return index < strings.length;
}
@Override
public String next() {
return strings[index++];
}
};
}
public static void main(String[] args) {
NoCollectionSequence n = new NoCollectionSequence();
InterfaceVSIterator.diapply(n.iterator());
/*
a b c
*/
}
}
/*
可以看到继承Collection接口必须实现iterator()方法,还有其他一些方法,而迭代器方法,只需添加一个迭代器方法就可以。
*/
11.13 Foreach与迭代器
foreach语法可以用于数组和所有实现了Iterable接口的类(包含所有的collection)。
// 所有的collection
public static void main(String[] args) {
Collection c = Arrays.asList("hello world".split(""));
for (Object o : c) {
System.out.print(o+" ");
}
System.out.println();
}
// 实现了Iterable接口
public class IterableClass implements Iterable {
private String[] strings ;
public IterableClass(String ...s){
strings = s;
}
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index =0;
@Override
public boolean hasNext() {
return index < strings.length;
}
@Override
public String next() {
return strings[index++];
}
};
}
public static void main(String[] args) {
IterableClass iterableClass = new IterableClass("a","b","c");
for (Object aClass : iterableClass) {
System.out.println(aClass);
}
}
}
public static void main(String[] args) {
// 获得当前系统的所有环境变量
Map<String, String> getenv = System.getenv();
for (Map.Entry entry:getenv.entrySet()){
System.out.println(entry);
}
}
// 虽然数组和实现了Iterable接口的类都可以使用foreach,但数组并不能被当成Iterable
public static void main(String[] args) {
String[] strings = {"a","b","c"};
// 下面写法会报错
//test(strings);
}
public static void test(Iterable it){
for (Object o : it) {
System.out.println(o);
}
}
11.13.1 适配器惯用法
实现向前遍历
public class RevArrayList extends ArrayList {
public Iterable revIterable(){
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
// 从最后一位开始
private int index =size()-1;
@Override
public boolean hasNext() {
//return index > -1;
return index >= 0;
}
@Override
public Object next() {
return get(index--);
}
};
}
};
}
}
public static void main(String[] args) {
RevArrayList revArrayList = new RevArrayList();
revArrayList.addAll(Arrays.asList("a","b","c"));
// 默认使用正循环
for (Object o : revArrayList) {
System.out.print(o +" ");
}
System.out.println();
for (Object o:revArrayList.revIterable()){
System.out.print(o+" ");
}
System.out.println();
}
/*
a b c
c b a
*/
向前遍历/向后遍历/随机遍历
public class MultiIterableClass implements Iterable {
String[] strings;
public MultiIterableClass(String... s) {
strings = s;
}
// 向后
@Override
public Iterator iterator() {
return new Iterator() {
private int index = 0;
@Override
public boolean hasNext() {
return index < strings.length;
}
@Override
public Object next() {
return strings[index++];
}
};
}
// 向前
public Iterable rev() {
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
private int index = strings.length - 1;
@Override
public boolean hasNext() {
return index >= 0;
}
@Override
public Object next() {
return strings[index--];
}
};
}
};
}
// 随机
public Iterable ran(){
return new Iterable() {
@Override
public Iterator iterator() {
List<String> list = Arrays.asList(strings);
Collections.shuffle(list);
return list.iterator();
}
};
}
}
public static void main(String[] args) {
MultiIterableClass m = new MultiIterableClass("a","b","c","d");
for (Object o : m) {
System.out.print(o);
}
System.out.println();
for (Object o:m.rev()){
System.out.print(o);
}
System.out.println();
for (Object o:m.ran()){
System.out.print(o);
}
}
/*
abcd
dcba
bdac
*/
集合的引用问题
public static void main(String[] args) {
String[] strings = {"a","b","c","d"};
List<String> list1 = new ArrayList<>(Arrays.asList(strings));
System.out.println("随机前:"+list1);
Collections.shuffle(list1);
System.out.println("随机后:"+list1);
System.out.println("数组:"+Arrays.toString(strings));
List<String> list2 = Arrays.asList(strings);
System.out.println("随机前:"+list2);
Collections.shuffle(list2);
System.out.println("随机后:"+list2);
System.out.println("数组:"+Arrays.asList(strings));
}
/*
随机前:[a, b, c, d]
随机后:[c, b, a, d]
数组:[a, b, c, d]
随机前:[a, b, c, d]
随机后:[d, a, b, c]
数组:[d, a, b, c]
*/
// 总结:可以看到,当新建一个集合时,打乱的只是对数组的引用,并不会改变数组的位置,当直接引用数组时,此时集合的改变,会改变数组的位置。
11.14 总结
- 数组将索引与对象联系起来,保存类型明确的对象,可以是多维的,数组一旦生成,容量就不能改变;
- Collection保存单一元素,Map保存相关联的键值对。可以自动调整大小,不能持有基本类型,但是可以自动包装;
- List将索引与对象关联,是排好序的容器;
- 大量随机访问使用ArrayList,经常从表中间插入或删除,使用LinkedList;
- 各种Queue和栈的行为,由LinkedList提供支持;
- Map是将对象与对象相关联的设计。HashMap快速访问,TreeMap保持键处于排序状态,LinkedHashMap保持元素插入时的位置,同时通过散列提供快速访问能力。
- Set不接受重复元素。HashSet最快的查询速度,TreeSet保持元素处于排序,LinkedHashSet以插入顺序保存元素。
- 不要使用过时的Vector、Hashtable、Stack。