Java集合
1.1集合框架的优点
传统的容器(数组)在进行增、删等破坏性操作时,需要移动元素,可能导致性能问题;同时添加、删除等算法和具体业务耦合在一起,增加了程序开发的复杂度。
Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中
2.2Collection
Collection是java集合框架(collection-frame)中的顶层接口。
Collection接口是一个容器,容器中只能存储引用数据类型,建议存同一类型的引用类型,方便后续遍历等操作。
容器中的元素可以是有序的、可重复的,称为List接口
也可能是无序的、唯一的,称为Set接口。
1.2.1 集合的常用方法
* 增:add/addAll
* 删:clear/remove/removeAll/retainAll
* 改:
* 查:contains/containsAll/isEmpty/size
1 public static void main(String[] args) { 2 3 /** 4 * 增:add/addAll 5 * 删:clear/remove/removeAll/retainAll 6 * 改: 7 * 查:contains/containsAll/isEmpty/size 8 */ 9 10 Collection c1 = new ArrayList(); 11 12 // 追加 13 c1.add("apple"); // Object object = new String("apple"); 14 // c1.add(1); // Object object = new Integer(1); 15 c1.add("banana"); 16 System.out.println(c1); 17 18 // 追加一个集合 19 Collection c2 = new ArrayList(); 20 c2.add("java"); 21 c2.add("c++"); 22 c1.addAll(c2); 23 System.out.println(c1); 24 25 // clear 26 //c1.clear(); 27 28 // c1.remove("apple"); 29 // c1.removeAll(c2); 30 //c1.retainAll(c2); 31 //System.out.println(c1); 32 33 System.out.println(c1.contains("apple")); 34 c2.add("js"); 35 System.out.println(c1.containsAll(c2)); 36 // c1.clear(); 37 System.out.println(c1.isEmpty()); 38 // 返回集合元素的个数 39 System.out.println(c1.size()); 40 41 System.out.println(c1.equals(c2)); 42 43 }
1.2.2集合的遍历
关键字:Iterable
Iterable 可遍历的接口,集合接口继承于它,集合支持快速遍历。
// 快速遍历 // for-each // Object 表示元素类型 // item表示迭代变量 // c1表示集合 for (Object item : c1) { System.out.println(item.toString()); }
快速遍历的本质
Collection继承于Iterable接口,表示集合支持快速遍历。Iterable接口定义了一个方法iterator()用于获取集合的迭代器,是一个Iterator接口类型,iterator()内部返回一个实现类实现类Iterator接口。这个实现类一定具有hasNext和next方法用于判断是否有下一个元素和获取下一个元素。快速遍历就是基于迭代器工作的。
1 public static void main(String[] args) { 2 3 4 Collection c1 = new ArrayList(); 5 c1.add("apple"); 6 c1.add("banana"); 7 c1.add("coco"); 8 9 10 // 快速遍历 11 // for-each 12 // Object 表示元素类型 13 // item表示迭代变量 14 // c1表示集合 15 for (Object item : c1) { 16 System.out.println(item.toString()); 17 } 18 19 // 迭代器遍历(国内) 20 Iterator it = c1.iterator(); 21 while(it.hasNext()) { 22 Object item = it.next(); 23 System.out.println(item.toString()); 24 } 25 26 // 国外 27 for(Iterator it2=c1.iterator();it2.hasNext();) { 28 Object item = it2.next(); 29 System.out.println(item.toString()); 30 } 31 }
1.3 List接口
List 接口中的元素时有序的、可重复的。List接口中的元素通过索引(index)来确定元素的顺序。
有序的 collection(也称为序列)。可以对列表中每个元素的插入位置进行精确地控制。
用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素
1.3.1 List常用方法
增:add/addAll/add(index,el)/addAll(index,collection)
删:clear/remove/removeall/remove(index)
改:set(index,el)
查:get(index)/indexOf/lastIndexOf()
其他:contains/containsall/isEmpty/size
1 public static void main(String[] args) { 2 3 /** 4 * 增:add/addAll/add(index,el)/addAll(index,collection) 5 * 删:clear/remove/removeAll/remove(index) 6 * 改:set(index,el) 7 * 查:get(index)/indexOf/lastIndexOf() 8 * 其他:contains/containsAll/isEmpty/size 9 */ 10 List list1 = new ArrayList(); 11 // 添加元素 12 list1.add("apple"); 13 list1.add("banana"); 14 // 在指定位置添加元素 15 list1.add(0, "coco"); 16 17 System.out.println(list1); 18 19 List list2 = new ArrayList(); 20 list2.add("java"); 21 list2.add("c++"); 22 23 list1.addAll(1, list2); 24 System.out.println(list1); 25 26 // 删除 27 list1.remove(0); 28 System.out.println(list1); 29 30 // 修改 31 list1.set(0, "javax"); 32 System.out.println(list1); 33 34 // 查 35 System.out.println(list1.get(0)); 36 list1.add("apple"); 37 list1.add("apple"); 38 System.out.println(list1); 39 System.out.println(list1.indexOf("apple")); 40 System.out.println(list1.lastIndexOf("apple")); 41 }
1.3.2 接口遍历
ListIterator继承于Iterator,在Itertaor的基础上提供了以正向遍历集合,也可以以遍历集合。
hasNext/ next 以正向 遍历
hasPrevious/previous 以遍历
【1】for each 遍历
【2】普通for遍历
【3】集合迭代器遍历
(1)正向遍历
(2)逆向遍历
1 public static void main(String[] args) { 2 3 4 List list1 = new ArrayList(); 5 list1.add("apple"); 6 list1.add("banana"); 7 list1.add("coco"); 8 9 // 【1】快速遍历 10 System.out.println("--for each--"); 11 for (Object item : list1) { 12 System.out.println(item.toString()); 13 } 14 15 // 【2】普通for 16 System.out.println("--for--"); 17 for(int i=0;i<list1.size();i++) { 18 System.out.println(list1.get(i)); 19 } 20 21 // 【3】集合迭代器 22 System.out.println("--iterator--"); 23 Iterator it = list1.iterator(); 24 while(it.hasNext()) { 25 System.out.println(it.next()); 26 } 27 28 System.out.println("--list iterator--"); 29 // 正向遍历 30 ListIterator it2 = list1.listIterator(); 31 while(it2.hasNext()) { 32 System.out.println(it2.next()); 33 } 34 35 // 逆序遍历 36 while(it2.hasPrevious()) { 37 System.out.println(it2.previous()); 38 } 39 40 System.out.println("--list iterator with index--"); 41 ListIterator it3 = list1.listIterator(1); 42 while(it3.hasNext()) { 43 System.out.println(it3.next()); 44 } 45 }
1.4 数据结构
1.4.1线性表
线性表数据按照一定的逻辑顺序存储在内存中。线性表是有序的。线性表根据内存的物理结构分为两种:数组和链表
数组是一种逻辑上有序的线性表,物理上也连续。
链表是一种逻辑上有序的线性表,但物理上不连续。
数组和链表的区别:
相同点:逻辑上有序
不同点:数组是物理上有序的,链表是物理上无序的。
数组在查询时效率高,在添加、删除元素时效率低(涉及移动元素)
链表在查询时效率低(每次从头开始,不能跳跃访问),在添加、删除元素时效率高(不涉及移动元素)
1.4.2 栈
特性:先进后出,后进先出
1.4.3 队列
特性:先进先出
1.5 ArrayList/Vector
ArayList 是List接口的实现类,底层数据结构是数组,实现大小可变的数组。
ArrayList 线程不安全,jdk1.2
ArrayList底层数据结构是数组,默认数组大小是10,如果添加的元素个数超过默认容量,
ArrayList会自动拓容,拓容原则:newCapacity = oldCapacity + oldCapacity / 2(oldCapacity>>1);
如果未来确定序列的元素不再增加,通过trimTosize()调制容量至合适的空间。
ArrayList 作为List接口的实现类,常用方法和遍历方法参考List接口。
Vector 是List接口的实现类,底层数据 结构也是数组 ,也是大小可变的数组 。
Vector是线程安全的,jdk1.0
Vector底层数据结构是数组,默认数组大小是10,
如果添加的元素个数超过默认容量,Vector会自动拓容,拓容原则:newCapacity = oldCapacity +capacityIncrement(增长因子);如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。
注意:Vector 在实现List接口的同时,同添加了自身特有的方法xxxElement,未来使用时为了程序的可拓展性,一定要按照接口来操作Vector。
1.6 Linkedlist
LInkedLIst是LIst接口的实现类,底层数组结构是链表。
LinkedList常用方和遍历方法参照List接口。
LinkedList线程不安全。
LinkedList坠子实现List接口,还实现栈接口。
push入栈操作,pop出栈操作
1 public class Test01 { 2 public static void main(String[] args) { 3 LinkedList list = new LinkedList(); 4 list.push("apple"); 5 list.push("banana"); 6 list.push("coco"); 7 8 9 System.out.println(list.pop()); 10 System.out.println(list.pop()); 11 System.out.println(list.pop()); 12 13 // java.util.NoSuchElementException 14 System.out.println(list.pop()); 15 } 16 }
队列(Queue)接口
add/remove/element()可能会出现NoSuchElementException异常
1 public static void main(String[] args) { 2 3 LinkedList queue = new LinkedList(); 4 // 入队 5 /** 6 * 队列头 队列尾 7 *<----- <----- 8 * [apple, banana, coco] 9 */ 10 queue.add("apple"); 11 queue.add("banana"); 12 queue.add("coco"); 13 System.out.println(queue); 14 15 // 出队 16 System.out.println(queue.remove()); 17 System.out.println(queue.remove()); 18 System.out.println(queue.remove()); 19 System.out.println(queue); 20 21 // java.util.NoSuchElementException 22 System.out.println(queue.remove()); 23 24 25 // 获取表头元素 26 System.out.println(queue.element()); 27 }
offer/poll/peek可能会返回特殊值(null)
1 public static void main(String[] args) { 2 3 LinkedList queue = new LinkedList(); 4 // 入队 5 /** 6 * 队列头 队列尾 7 *<----- <----- 8 * [apple, banana, coco] 9 */ 10 queue.offer("apple"); 11 queue.offer("banana"); 12 queue.offer("coco"); 13 14 // 出队列 15 //System.out.println(queue.poll()); 16 //System.out.println(queue.poll()); 17 //System.out.println(queue.poll()); 18 System.out.println(queue); 19 20 //System.out.println(queue.poll()); 21 22 // 获取表头元素 23 System.out.println(queue.peek()); 24 25 }
双向队列(Deque)接口
1 /** 2 * 以双向队列形式操作LinkedList 3 */ 4 public class Test04 { 5 public static void main(String[] args) { 6 7 LinkedList queue = new LinkedList(); 8 // 入队 9 /** 10 *<----- <----- 11 * [apple, banana, coco] 12 * ----> -----> 13 */ 14 15 queue.addFirst("apple"); 16 queue.addFirst("banana"); 17 queue.addFirst("coco"); 18 System.out.println(queue); 19 20 System.out.println(queue.removeLast()); 21 System.out.println(queue.removeFirst()); 22 System.out.println(queue.removeFirst()); 23 System.out.println(queue); 24 25 // 获取头元素 26 System.out.println(queue.getFirst()); 27 28 } 29 }
1.7 Iterator和LIstiterator
Iterator在迭代过程中不允许向集合中添加元素
Listiterator可以在迭代过程中添加元素
1 public static void main(String[] args) { 2 ArrayList list = new ArrayList(); 3 list.add("apple"); 4 list.add("banana"); 5 list.add("coco"); 6 7 Iterator it = list.iterator(); 8 while(it.hasNext()) { 9 String item = (String) it.next(); 10 if(item.equals("banana")) { 11 list.add("test"); 12 } 13 } 14 15 System.out.println(list); 16 }
当通过Iterator集合迭代器遍历集合过程中,不能再向集合汇总添加元素,否则出现ConcurrentModificationException 并发修改异常。
ListIterator允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。
1 public class Test01 { 2 public static void main(String[] args) { 3 ArrayList list = new ArrayList(); 4 list.add("apple"); 5 list.add("banana"); 6 list.add("coco"); 7 8 ListIterator it = list.listIterator(); 9 while(it.hasNext()) { 10 String item = (String) it.next(); 11 if(item.equals("banana")) { 12 it.add("test"); 13 } 14 } 15 16 System.out.println(list); 17 } 18 }
1.8 泛型(generic)
1.8.1 反省的概念
泛型允许开发者在强类型程序设计 语言(java)编写代码时定义一些可变部分,这些部分在使用前必须作出指明。
泛型就是将类型参数化。
ArrayList<E> list 表示声明了一个列表list,列表的元素是E类型。
ArrayList<String> list = new ArrayList<String>();
声明了一个列表list,列表的元素只能是String类型。
泛型在编译器起作用,运行时jvm察觉不到泛型的存在。
1.8.2 泛型的擦除
泛型在运行时已经被擦除了。
1 public static void main(String[] args) { 2 ArrayList<String> list = new ArrayList<String>(); 3 list.add("apple"); 4 System.out.println(list instanceof ArrayList); 5 System.out.println(list instanceof ArrayList<String>);//错的表达方式 6 System.out.println(list instanceof ArrayList<?>);//正确的表达方式 7 }
错误时的异常:
Cannot perform instanceof check against parameterized type ArrayList<String>. Use the form ArrayList<?> instead since further generic type information will be erased at runtime
1.8.3 泛型的应用
1.8.3.1 泛型类
当一个类中属性的数据类型不确定时,具体是什么类型由开发者决定,使用泛型。泛型类的形式:
public class 类名<T> { }
定义一个泛型类
1 public class FanClass<T> { 2 private T t; 3 4 public T getT() { 5 return t; 6 } 7 8 public void setT(T t) { 9 this.t = t; 10 } 11 12 public FanClass(T t) { 13 super(); 14 this.t = t; 15 } 16 17 public FanClass() { 18 super(); 19 } 20 }
1 public class Test01 { 2 public static void main(String[] args) { 3 FanClass<String> fan = new FanClass<String>(); 4 fan.setT("apple"); 5 6 FanClass<Integer> fan2 = new FanClass<Integer>(); 7 fan2.setT(1); 8 } 9 }
1.8.3.2 泛型方法
当现代战争方法的参数 类型不确定时,具体是什么类型由使用者来确定,可以考虑使用泛型方法。
形式:
【1】把类定义成泛型类,如下:
public <T> void xxx(T a) { System.out.println(a); }
【2】
1 public class Student { 2 3 4 /*public void showInfo(int a) { 5 System.out.println(a); 6 } 7 8 public void showInfo(float a) { 9 System.out.println(a); 10 } 11 12 public void showInfo(String a) { 13 System.out.println(a); 14 }*/ 15 16 public <T> void showInfo(T a) { 17 System.out.println(a); 18 } 19 }
1 public static void main(String[] args) { 2 3 Student stu = new Student(); 4 stu.showInfo(1); 5 stu.showInfo("apple"); 6 stu.showInfo(1.0f); 7 }
泛型方法在调用时确定(指明)类型。
泛型方法在一定程度上优化了方法重载。
泛型方法可以定义多个泛型类型
1 // 可以定义多个泛型的类型 2 public <A,B> void showInfo(A a,B b) { 3 System.out.println(a); 4 System.out.println(b); 5 }
多个泛型类型进一步优化了方法重载。
多个同类型的泛型
1 // 多个同类型的泛型 2 /*public <A> void print(A a) { 3 System.out.println(a); 4 } 5 public <A> void print(A a,A b) { 6 System.out.println(a); 7 System.out.println(b); 8 }*/ 9 10 public <A> void print(A...a) { 11 System.out.println(a); 12 }
A… a 表示方法可以接受多个参数。当调用方法传递多个参数时,多个参数被放到a数组中,a是什么类型的数组由开发者调用处传参决定。
1 stu.print(1); 2 stu.print(1,2); 3 4 stu.print("apple"); 5 stu.print("apple","banana");
print(A...a) 方法称为可变参数的泛型形式。
2.1.1.1 泛型接口(C)
如果接口中的方法的参数(形参、返回值)不确定时,可以考虑使用泛型接口。形式
1 public interface FanInterface<T> { 2 3 public void showInfo(T t); 4 5 }
[1]实现类能确定泛型接口的类型
1 public class ImplClass implements FanInterface<String>{ 2 3 @Override 4 5 public void showInfo(String t) { 6 7 // TODO Auto-generated method stub 8 9 } 10 11 }
[2]实现类不能确定泛型接口的类型->继续泛。
1 public class ImplClass2<T> implements FanInterface<T>{ 2 3 @Override 4 5 public void showInfo(T t) { 6 7 8 } 9 10 }
2.1.1.2 泛型的上限和下限 (C)
1 public static void print(ArrayList<? extends Pet> list) { 2 3 for (Pet pet : list) { 4 5 pet.showInfo(); 6 7 } 8 9 }
泛型的上限ArrayList<? extends Pet> list 声明了一个容器,容器中的元素类型一定要继承于Pet,我们称这种形式叫做泛型的上限。
泛型的下限ArrayList<? super Pet> list 声明了一个容器,容器中的元素类型一定要是Pet的父类,我们称这个形式为泛型的下限。
3.1 Set接口
Set接口表示一个唯一、无序的容器(和添加顺序无关)
3.1.1 Set接口提供的方法
1 public static void main(String[] args) { 2 3 /** 4 5 * 增:add/addAll 6 7 * 删:clear/remove/removeAll/retainAll 8 9 * 改: 10 11 * 查:contains/containsAll 12 13 * 遍历:iterator 14 15 * 其他:size/isEmpty 16 17 */ 18 19 20 21 Set<Integer> set = new HashSet<Integer>(); 22 23 // [1]添加 24 25 // 无序 26 27 set.add(10); 28 29 set.add(3); 30 31 set.add(20); 32 33 set.add(0); 34 35 // 不能添加重复元素 36 37 boolean r = set.add(1); 38 39 System.out.println(set); 40 41 42 43 // 【2】删除 44 45 // set.remove(1); 46 47 // set.clear(); 48 49 // System.out.println(set); 50 51 52 53 // 【3】查看是否包含 54 55 System.out.println(set.contains(1)); 56 57 58 59 // 【4】其他 60 61 System.out.println(set.size()); 62 63 System.out.println(set.isEmpty()); 64 65 }
3.1.2 Set接口的遍历
1 public static void main(String[] args) { 2 3 4 Set<String> set = new HashSet<String>(); 5 6 set.add("banana"); 7 8 set.add("apple"); 9 10 set.add("coco"); 11 12 13 14 // 快速遍历 15 16 for (String item : set) { 17 18 System.out.println(item); 19 20 } 21 22 23 // 迭代器 24 25 Iterator<String> it = set.iterator(); 26 27 while(it.hasNext()) { 28 29 String item = it.next(); 30 31 System.out.println(item); 32 33 } 34 35 }
Set接口的实现类常见的有HashSet、LinkedHashSet、TreeSet
3.2 HashSet
HashSet是Set接口的实现类,底层数据结构是哈希表。
HashSet是线程不安全的(不保证同步)
3.2.1 哈希表工作原理
3.2.2 添加自定义对象
根据哈希表的工作原理,请存储一个自定义对象到HashSet中。
1 package cn.sxt03.hashset; 2 3 4 5 public class Student { 6 7 private String id; 8 9 private String name; 10 11 private int age; 12 13 14 15 // … 16 17 18 19 20 21 @Override 22 23 public int hashCode() { 24 25 final int prime = 31; 26 27 int result = 1; 28 29 result = prime * result + age; 30 31 result = prime * result + ((id == null) ? 0 : id.hashCode()); 32 33 result = prime * result + ((name == null) ? 0 : name.hashCode()); 34 35 return result; 36 37 } 38 39 40 41 @Override 42 43 public boolean equals(Object obj) { 44 45 if (this == obj) 46 47 return true; 48 49 if (obj == null) 50 51 return false; 52 53 if (getClass() != obj.getClass()) 54 55 return false; 56 57 Student other = (Student) obj; 58 59 if (age != other.age) 60 61 return false; 62 63 if (id == null) { 64 65 if (other.id != null) 66 67 return false; 68 69 } else if (!id.equals(other.id)) 70 71 return false; 72 73 if (name == null) { 74 75 if (other.name != null) 76 77 return false; 78 79 } else if (!name.equals(other.name)) 80 81 return false; 82 83 return true; 84 85 } 86 87 88 89 @Override 90 91 public String toString() { 92 93 return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; 94 95 } 96 97 98 99 }
总结
[1]如果向HashSet中存储元素时,元素一定要实现hashCode方法和equals方法。
[2] 优点:添加、删除、查询效率高;缺点:无序
3.3 LinkedHashSet
LinkedHashSet是Set接口的实现类,底层数据结构哈希表+链表
哈希表用于散列元素;链表用于维持添加顺序。
如果要添加自定义对象元素,也需要重写hashCode和equals方法。
3.4 TreeSet
TreeSet 是Set接口的实现类,底层数据结构是二叉树。
TreeSet 存储的数据按照一定的规则存储。存储规则让数据表现出自然顺序。
3.4.1 TreeSet工作原理
添加一个新元素t的存储的步骤:
[1] 如果集合无元素,t直接加入;如果集合有元素,t和根节点比较;
[2] 如果t小于根节点;把t放到根节点的左子树上;重复1-3步骤
[3] t大于根节点;把t放到根节点的右子树上;重复1-3步骤
输出时按照一定的规则:左子树->根节点->右子树
根据TreeSet的工作原理,向TreeSet添加自定义元素?
向TreeSet中添加元素时,一定要提供比较策略,否则会出现ClassCastException。
比较策略分两种:内部比较器和外部比较器
3.4.2 内部比较器
当一个自定义对象实现Comparable并实现compareTo方法时,通过指定具体的比较策略,此时称为内部比较器。
1 package cn.sxt05.treeset; 2 3 4 5 public class Student implements Comparable<Student>{ 6 7 private String id; 8 9 private String name; 10 11 private int age; 12 13 14 15 // 。。。 16 17 18 19 @Override 20 21 public String toString() { 22 23 return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; 24 25 } 26 27 28 29 @Override 30 31 public int compareTo(Student o) { 32 33 if(this.getAge()<o.getAge()) { 34 35 return -1; 36 37 }else if(this.getAge() == o.getAge()) { 38 39 return 0; 40 41 }else { 42 43 return 1; 44 45 } 46 47 } 48 49 50 51 }
比较策略的几种情况
[1]比较策略一般当前对象写在前面,待比较对象也在后面,比较结果默认升序
return this.getAge() - o.getAge() ;
如果想要降序,改变两个比较对象的位置即可。
[2] 多种比较因素
1 @Override 2 3 public int compareTo(Student o) { 4 5 /*if(this.getAge()<o.getAge()) { 6 7 return -1; 8 9 }else if(this.getAge() == o.getAge()) { 10 11 return 0; 12 13 }else { 14 15 return 1; 16 17 }*/ 18 19 20 21 // return this.getAge() - o.getAge() ; 22 23 24 if(this.getAge()<o.getAge()) { 25 26 return -1; 27 28 }else if(this.getAge() == o.getAge()) { 29 30 return this.getName().compareTo(o.getName()); 31 32 }else { 33 34 return 1; 35 36 } 37 38 }
3.4.3 外部比较器
当实际开发过程中不知道添加元素的源代码、无权修改别人的代码,此时可以使用外部比较器。
Comparator 位于java.util包中,定义了compare(o1,o2) 用于提供外部比较策略。
TreeSet接受一个指定比较策略的构造方法,这些比较策略的实现类必须实现Comparator
接口。
需求:按照字符串的长度比较
1 public class Test01 { 2 3 public static void main(String[] args) { 4 5 6 LenComparator lenComparator = new LenComparator(); 7 8 TreeSet<String> set2 = new TreeSet<String>(lenComparator); 9 10 11 12 set2.add("banana"); 13 14 set2.add("coco"); 15 16 set2.add("apple"); 17 18 set2.add("apple"); 19 20 System.out.println(set2); 21 22 23 24 } 25 26 } 27 28 29 class LenComparator implements Comparator<String>{ 30 31 32 33 @Override 34 35 public int compare(String o1, String o2) { 36 37 return o1.length() - o2.length(); 38 39 } 40 41 }
使用匿名内部类优化
1 public class Test02 { 2 3 public static void main(String[] args) { 4 5 6 TreeSet<String> set2 = new TreeSet<String>(new Comparator<String>() { 7 8 9 @Override 10 11 public int compare(String o1, String o2) { 12 13 return o1.length() - o2.length(); 14 15 } 16 17 18 }); 19 20 set2.add("banana"); 21 22 set2.add("coco"); 23 24 set2.add("apple"); 25 26 27 set2.add("apple"); 28 29 System.out.println(set2); 30 31 32 } 33 34 }
3.5 Map接口
Map接口称为键值对集合或者映射集合,其中的元素(entry)是以键值对(key-value)的形式存在。
Map 容器接口中提供了增、删、改、查的方式对集合进行操作。
Map接口中都是通过key来操作键值对,一般key是已知。通过key获取value。
3.5.1 map常用方法
1 public static void main(String[] args) { 2 3 4 5 /** 6 7 * 增:put/putAll 8 9 * 删:clear/remove 10 11 * 改:put 12 13 * 查:get/containsKey/containsValue 14 15 * 其他:isEmpty/size 16 17 */ 18 19 20 21 Map<String, String> map = new HashMap<String,String>(); 22 23 24 25 // 【1】put 26 27 map.put("A", "apple"); 28 29 map.put("B", "banana"); 30 31 map.put("C", "coco"); 32 33 34 35 // 【2】删除 36 37 // map.clear(); 38 39 // map.remove("A"); 40 41 42 43 // 【3】修改 44 45 //map.put("A", "apple x"); 46 47 48 49 // 【4】查看 50 51 String val = map.get("A"); 52 53 System.out.println(map.containsKey("D")); 54 55 56 57 58 59 System.out.println(map); 60 61 }
3.5.2 map接口的遍历
通过keySet() 返回map中键的set集合。
1 public static void main(String[] args) { 2 3 4 5 Map<String, String> map = new HashMap<String,String>(); 6 7 8 9 map.put("B", "banana"); 10 11 map.put("A", "apple"); 12 13 map.put("C", "coco"); 14 15 // map无序 16 17 // 可以根据key的自然顺序 让map有序 => 一般用string作为key 18 19 System.out.println(map); 20 21 22 23 24 25 // 遍历 26 27 Set<String> keys = map.keySet(); 28 29 for (String key : keys) { 30 31 System.out.println(key+"=>"+map.get(key)); 32 33 } 34 35 36 Iterator<String> it = keys.iterator(); 37 38 while(it.hasNext()) { 39 40 String key = it.next(); 41 42 System.out.println(key+"=>"+map.get(key)); 43 44 } 45 46 }
map中以键值对作为元素,键值对在map中称为entry,entrySet返回键值对的set集合。
1 public static void main(String[] args) { 2 3 4 5 Map<String, String> map = new HashMap<String,String>(); 6 7 8 9 map.put("B", "banana"); 10 11 map.put("A", "apple"); 12 13 map.put("C", "coco"); 14 15 // map无序 16 17 // 可以根据key的自然顺序 让map有序 => 一般用string作为key 18 19 System.out.println(map); 20 21 22 // entrySet 23 24 Set<Entry<String, String>> entrySet = map.entrySet(); 25 26 for (Entry<String, String> entry : entrySet) { 27 28 System.out.println(entry.getKey()+"=>"+entry.getValue()); 29 30 } 31 32 33 Iterator<Entry<String, String>> it2 = entrySet.iterator(); 34 35 while(it2.hasNext()) { 36 37 Entry<String, String> entry = it2.next(); 38 39 System.out.println(entry.getKey()+"=>"+entry.getValue()); 40 41 } 42 43 }
Map接口的实现类HashMap、LinkedHashMap、TreeMap
3.6 HashMap
HashMap 是Map的实现类,key以HashSet存储。
1 public static void main(String[] args) { 2 3 4 5 /* 6 7 HashMap<String, Object> map = new HashMap<String,Object>(); 8 9 10 11 ArrayList<String> list1 = new ArrayList<String>(); 12 13 list1.add("alex"); 14 15 list1.add("alice"); 16 17 list1.add("allen"); 18 19 map.put("A", list1); 20 21 22 23 24 25 ArrayList<String> list2 = new ArrayList<String>(); 26 27 list2.add("ben"); 28 29 list2.add("bill"); 30 31 map.put("B", list2); 32 33 34 35 System.out.println(map); 36 37 */ 38 39 40 41 42 43 HashMap<Student, Object> map = new HashMap<Student,Object>(); 44 45 46 47 ArrayList<String> list1 = new ArrayList<String>(); 48 49 list1.add("alex"); 50 51 list1.add("alice"); 52 53 list1.add("allen"); 54 55 Student s1 = new Student("001", "大狗", 20); 56 57 map.put(s1, list1); 58 59 60 61 ArrayList<String> list2 = new ArrayList<String>(); 62 63 list2.add("ben"); 64 65 list2.add("bill"); 66 67 Student s2 = new Student("001", "大狗", 20); 68 69 // 修改 70 71 map.put(s2, list2); 72 73 System.out.println(map); 74 75 76 77 }
总结:
[1] 向HashMap中存储元素时,key一定要实现hashCode和equals(用于去重,用以符合哈希表工作原理)
[2] 一般建议使用String作为Map接口的key
3.7 LinkedHashMap
LinkedHashMap是Map接口的实现类,key以LinkedHashSet存储。
哈希表散列key,链表维持key的添加顺序。
1 public static void main(String[] args) { 2 3 /*LinkedHashMap<String, Object> map = new LinkedHashMap<String,Object>(); 4 5 ArrayList<String> list2 = new ArrayList<String>(); 6 7 list2.add("ben"); 8 9 list2.add("bill"); 10 11 map.put("B", list2); 12 13 14 15 ArrayList<String> list1 = new ArrayList<String>(); 16 17 list1.add("alex"); 18 19 list1.add("alice"); 20 21 list1.add("allen"); 22 23 map.put("A", list1); 24 25 26 27 System.out.println(map);*/ 28 29 30 HashMap<Student, Object> map = new HashMap<Student,Object>(); 31 32 ArrayList<String> list1 = new ArrayList<String>(); 33 34 list1.add("alex"); 35 36 list1.add("alice"); 37 38 list1.add("allen"); 39 40 Student s1 = new Student("001", "大狗", 20); 41 42 map.put(s1, list1); 43 44 45 46 ArrayList<String> list2 = new ArrayList<String>(); 47 48 list2.add("ben"); 49 50 list2.add("bill"); 51 52 Student s2 = new Student("001", "大狗", 20); 53 54 // 修改 55 56 map.put(s2, list2); 57 58 System.out.println(map); 59 60 61 62 }
1.8 TreeMap
TreeMap是Map的实现类,key以TreeSet存储。
1 public static void main(String[] args) { 2 3 4 5 6 7 /*TreeMap<String, Object> map = new TreeMap<String,Object>(new Comparator<String>() { 8 9 10 11 @Override 12 13 public int compare(String o1, String o2) { 14 15 return o1.length() - o2.length(); 16 17 } 18 19 }); 20 21 22 23 ArrayList<String> list2 = new ArrayList<String>(); 24 25 list2.add("ben"); 26 27 list2.add("bill"); 28 29 map.put("Aa", list2); 30 31 32 33 ArrayList<String> list1 = new ArrayList<String>(); 34 35 list1.add("alex"); 36 37 list1.add("alice"); 38 39 list1.add("allen"); 40 41 map.put("B", list1); 42 43 44 45 System.out.println(map);*/ 46 47 48 49 50 51 52 53 TreeMap<Student, Object> map = new TreeMap<Student,Object>(new Comparator<Student>() { 54 55 56 57 @Override 58 59 public int compare(Student o1, Student o2) { 60 61 return o1.getAge() - o2.getAge(); 62 63 } 64 65 }); 66 67 68 69 ArrayList<String> list1 = new ArrayList<String>(); 70 71 list1.add("alex"); 72 73 list1.add("alice"); 74 75 list1.add("allen"); 76 77 Student s1 = new Student("001", "大狗", 20); 78 79 map.put(s1, list1); 80 81 82 83 ArrayList<String> list2 = new ArrayList<String>(); 84 85 list2.add("ben"); 86 87 list2.add("bill"); 88 89 Student s2 = new Student("001", "2狗", 20); 90 91 // 修改 92 93 map.put(s2, list2); 94 95 System.out.println(map); 96 97 98 99 }
1.9 总结