目录
Collection
- Collection和Collections的区别?
Collection:是单列集合的顶层接口,有子接口List和Set。(Map是双列的)
Collections:是针对集合操作的工具类,有对集合进行排序和二分查找的方法
List和Set
- 在实际开发中,需要将使用的对象储存于特定数据结构的容器中。JDK提供了这样的容器---集合(Collection)。
- Collection下面有两个常用的子接口:
java.util.List: 线性表,是一类可以存放重复元素并且有序的集合,可以通过下标操作元素。
java.util.Set: 不可重复集,大部分的实现类是无序的。 元素是否重复是依靠元素自身equals比较的结果而定。
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo1 {
public static void main(String[] args) {
Collection c = new ArrayList();
/**
* boolean add(E e) 向当前集合中添加元素,若元素成功添加则返回true
*/
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c);
/**
* 获取集合元素个数
*/
int size = c.size();
System.out.println(size);
/**
* boolean isEmpty() 判断当前集合是否为空集(不含有任何元素)
*/
boolean isEmpty = c.isEmpty();
System.out.println("是否为空集:" +isEmpty);
/**
* void clear() 清空集合
*/
c.clear();
System.out.println(c);
System.out.println(c.size());
System.out.println(c.isEmpty());
}
}
集合方法
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo2 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(3,4));
c.add(new Point(5,6));
c.add(new Point(7,8));
/**
* 集合重写了toString方法,并且集合中每个元素输出的内容就是元素自身toString的内容。
*/
System.out.println(c);
Point p = new Point(1,2);
/**
* 集合在判断给定元素是否被包含时是依靠元素equals的比较结果判定的。
*/
boolean contains = c.contains(p);
System.out.println(contains);
/**
* remove只删一次,重写equals
*/
c.remove(p);
System.out.println(c);
}
}
集合持有对象的引用
- 集合中存储的都是引用类型元素,并且集合只保存每个元素对象的引用,而并非将元素对象本身存入集合。
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo3 {
public static void main(String[] args) {
Point p = new Point(1, 2);
Collection c = new ArrayList();
c.add(p);// 将p变量(地址)存入了集合
System.out.println("p:" + p);
System.out.println("c:" + c);
p.setX(2);
System.out.println("p:" + p);
System.out.println("c:" + c);// [(2,2)]
}
集合间操作方法
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class CollectionDemo4 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("c++");
c1.add("java");
c1.add("php");
System.out.println(c1);
Collection c2 = new HashSet();
c2.add("android");
c2.add("ios");
System.out.println(c2);
/**
* boolean addAll(Collection c)
* 将给定元素中的所有元素添加到当前集合中,
* 添加后当前集合有变化时就返回true。
*/
c1.addAll(c2);
System.out.println(c1);
Collection c3 = new ArrayList();
c3.add("java");
c3.add("ios");
c3.add("c");
/**
* boolean containsAll(Collection c)
* 判断当前集合是否包含给定集合中的所有元素
*/
boolean containsAll = c1.containsAll(c3);
System.out.println(containsAll);
/**
* 删除当前集合中与给定集合共有的元素
*/
c1.removeAll(c3);
System.out.println(c1);
System.out.println(c3);
}
}
Iterator迭代器
- 迭代器用于遍历集合元素。获取迭代器可以使用Collection定义的方法:
Iterator iterator().//该方法获取一个可以遍历当前集合的迭代器实现类
- 迭代器Iterator是一个接口,规定了迭代器实现类遍历集合的操作,我们无须记住每种集合的迭代器的名字,只需要当它们是迭代器即可。
- 迭代器遍历集合遵循的模式:问->取->删。其中删除元素不是必要操作。
- boolean hasNext() 方法:判断集合是否还有元素可以遍历。
- E next()方法: 遍历集合下一个元素。
- 迭代器有一个要求,在使用迭代器遍历集合的过程中,不得使用集合自身的方法增删元素,否则遍历过程中会抛出异常。
- 迭代器的remove方法不需要传入参数,删除的是本次迭代时通过next获取的元素。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo5 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("one");
c.add("#");
c.add("two");
c.add("#");
c.add("three");
c.add("#");
c.add("four");
c.add("#");
c.add("five");
System.out.println(c);
Iterator it = c.iterator();
/**
* boolean hasNext()
* 判断集合是否还有元素可以遍历
*/
while(it.hasNext()) {
/**
* E next()
* 遍历集合下一个元素
*/
String str = (String)it.next();
System.out.println(str);
if("#".equals(str)) {
/**
* 迭代器有一个要求,在使用迭代器遍历集合的过程中
* 不得使用集合自身的方法增删元素,否则遍历过程中会抛出异常。
*/
// c.remove(str);
/**
* 迭代器的remove方法不需要传入参数,
* 删除的是本次迭代时通过next获取的元素
*/
it.remove();
}
}
System.out.println(c);
}
}
增强型for循环
- JDK5之后推出了一个特性:增强型for循环,也称为新循环,for each。
- 新循环不取代传统for循环的工作,使用它是为了遍历集合或数组的。
- 新循环是编译器认可的语法,在编译为字节码文件时会将它改为普通for循环遍历数组。
public class NewForDemo1 {
public static void main(String[] args) {
String[] array = {"one","two","three","four","five"};
for(int i = 0; i < array.length; i++) {
String s = array[i];
System.out.println(s);
}
/**
* 新循环是编译器认可的语法,在编译为字节码文件时会将它改为普通for循环遍历数组。
*/
for(String s: array) {
System.out.println(s);
}
}
}
- 新循环遍历集合会被编译器改为迭代器,因此不要在便历的过程中通过集合的方法增删元素,否则会抛出异常!
import java.util.ArrayList;
import java.util.Collection;
public class NewForDemo2 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
/**
* 新循环遍历集合会被编译器改为迭代器,因此不要在便历的过程中通过集合的方法增删元素,否则会抛出异常!
*/
for(Object o:c) {
String str = (String)o;
System.out.println(str);
}
}
}
泛型机制
- 泛型是JDK5之后推出的又一个特性,也称为参数化类型,是允许我们在使用一个类时指定其事先定义的某个属性或者方法的参数、返回值的类型。这使得我们使用更灵活。
- 泛型应用最广的地方就是集合,用来在使用集合时指定其元素的类型。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo6 {
public static void main(String[] args) {
// 创建集合时指定泛型(这里用于约束元素类型)
Collection<String> c = new ArrayList<String>();
// 此时向当前集合添加元素时编译器会检查元素类型
c.add("one");
c.add("two");
c.add("three");
c.add("four");
// 如果元素类型与泛型不匹配,编译不通过。
// c.add(1);
System.out.println(c);
/**
* 使用新循环遍历时,可以直接使用String接收了。
*/
for(String s: c) {
System.out.println(s);
}
// 迭代器的泛型与集合一致即可
Iterator<String> it = c.iterator();
while(it.hasNext()) {
// 获取时不需要再造型(编译器编译时会补上造型)
String str = it.next();
System.out.println(str);
}
}
}
List接口
List(ArrayList和LinkedList)
- List接口是Collection的子接口,用于定义线性表数据结构。可以将List理解为存放对象的数组,只不过元素个数可以动态的增加或减少。
- List集合是Collection非常常用的子接口,规定了可重复集并且有序的集合。特点是可以通过下标操作元素。
- 常用实现类:
java.util.ArrayList:内部用数组实现,查询性能更好
java.util.LinkedList:内部由链表实现,增删性能更好
- 可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别。ArrayList更适合于随机访问而LinkedList更适合于插入和删除。 对性能不是极端苛刻要求的话,一般使用ArrayList即可。
import java.util.ArrayList;
import java.util.List;
public class ListDemo1 {
public static void main(String[] args) {
// JDK7之后,后面new时的泛型可以用<>,类型可以忽略。
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
/**
* E get(int index) 获取指定下标处对应的元素
*/
String str = list.get(2);
System.out.println(str);
// 普通for循环可以遍历List集合
for(int i = 0; i < list.size(); i++) {
str = list.get(i);
System.out.println(str);
}
/**
* E set(int index,E e)
* 将给定元素设置到指定位置,返回值为原位置对应的元素。所以set是替换元素操作.
*/
String old = list.set(2, "2");
System.out.println(list);
System.out.println(old);
/**
* 在不创建新集合的前提下,将list元素倒序
*/
// int size = list.size();
for(int i = 0; i < list.size()/2; i++) {
list.set(i,list.set(list.size()-1-i, list.get(i)));
// String end = list.get(size-1-i);
// String start = list.set(i, end);
// list.set(size-1-i, start);
}
System.out.println(list);
}
}
List方法
import java.util.ArrayList;
import java.util.List;
public class ListDemo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
/**
* void add(int index,E e) 将给定元素插入到指定位置
*/
list.add(1, "2");
System.out.println(list);
/**
* E remove(int i) 删除指定位置对应的元素并将其返回。
*/
String old = list.remove(2);
System.out.println(list);
System.out.println(old);
}
}
List集合获取子集
import java.util.ArrayList;
import java.util.List;
public class ListDemo3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
System.out.println(list);
// 获取3-7
List<Integer> subList = list.subList(3, 8);
System.out.println(subList);
// 子集元素扩大10倍
for (int i = 0; i < subList.size(); i++) {
subList.set(i, subList.get(i) * 10);
}
System.out.println(subList);
/**
* 对子集元素操作就是对原集合对应元素的操作。
*/
System.out.println(list);
// 删除2-8
list.subList(2, 9).clear();
System.out.println(list);
}
}
List转化为数组
- List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都具备这个功能。
- 其有两个方法:
Object[] toArray().//不常用
<T>T[] toArray(T[] a).//常用
- 第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionToArrayDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c);
// Objectp[] array = c.toArray();需要每个元素都强转为String类型,太麻烦不用
String[] array = c.toArray(new String[c.size()]);// 长度随便写,任性的toArray,长度够就用,不够自己new。
System.out.println(array.length);
System.out.println(Arrays.toString(array));
}
}
数组转换为List
- Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转化为对应的List集合。
- 其方法定义为:
static <T>List<T> asList<T...a>
- 返回的List的几何元素由传入的数组的元素类型决定。
- 并且要注意的是,由于数组是定长的,返回的集合我们不能对其增删元素,对该集合元素操作就是对原数组元素操作,否则会抛出异常。并且对集合的元素进行修改会影响数组对应的元素。
- 如果希望对集合元素增删,需要自行创建一个集合并将该集合元素导入。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayToListDemo {
public static void main(String[] args) {
String[] array = {"one","two","three","four"};
List<String> list = Arrays.asList(array);
System.out.println(list);
list.set(1,"2");
System.out.println(list);
// 对该集合元素操作就是对原数组元素操作
System.out.println(Arrays.toString(array));
/**
* 由于数组是定长的,因此从数组转出来的集合是不能增删元素的,否则会抛出异常。
*/
// list.add("five"); add、remove都不支持此操作
/**
* 如果希望对集合元素增删,需要自行创建一个集合并将该集合元素导入
*/
List<String> list2 = new ArrayList<String>(list);
list2.add("five");
System.out.println(list2);
}
}
List排序
- Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序的sort方法。
- 方法定义:
void sort(List<T> list).//作用是对给定的集合元素进行自然排序(从小到大)。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
public class SortListDemo1 {
public static void main(String[] args) {
Random ran = new Random();
List<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; i++) {
list.add(ran.nextInt(100));
}
System.out.println(list);
Collections.sort(list);
System.out.println(list);
// 将集合中的数字从大到小排
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list);
}
}
- 该sort方法在排序集合时要求集合元素必须实现一个接口:Comparable,比较接口(比较器)。
- 该接口有一个抽象方法,实现的目的是定义对象间比较大小的规则, 只有实现了这个接口的元素才能比较大小,从而排序。
- 当我们调用某个功能时,该功能反过来要求我们为其修改其他地方的代码,这种情况称为"侵入性"。实际开发中我们应当尽量避免高侵入性的性能。
- 抽象方法定义:
int compare(T t);
- 该方法用于使当前对象与给定对象进行比较:
若当前对象大于给定对象,那么返回值应为>0的整数。
若小于给定对象,那么返回值应为<0的整数。
若两个对象相等,则应返回0。
- 当集合元素已经实现了Comparable接口,但是其定义的比较规则不满足我们的排序要求时,我们也可以自行提供一个比较器来完成排序。
- Comparator接口要求实现类必须重写其定义的方法:
int Comparator(T o1,T o2).//该方法作用是定义o1,o2的大小规则,返回值关心的是取值范围.
- 该方法的返回值要求:
返回值>0时,表示o1>o2;
返回值<0时,表示o1<o2;
返回值=0时,表示o1=o2.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortListDemo2 {
public static void main(String[] args) {
List<Point> list = new ArrayList<Point>();
list.add(new Point(2,4));
list.add(new Point(10,25));
list.add(new Point(10,10));
list.add(new Point(6,3));
list.add(new Point(70,5));
list.add(new Point(2,9));
list.add(new Point(10,4));
System.out.println(list);
/**
* 该sort方法在排序集合时要求集合元素必须实现一个接口:Comparable,比较接口(比较器)。
* 该接口有一个抽象方法,实现的目的是定义对象间比较大小的规则,
* 只有实现了这个接口的元素才能比较大小,从而排序。
*
* 当我们调用某个功能时,该功能反过来要求我们为其修改其他地方的代码,这种情况称为"侵入性"。
* 实际开发中我们应当尽量避免高侵入性的性能。
*
*/
Collections.sort(list, new Comparator<Point>() {
/**
* 实现Comparator接口后必须重写compare方法,该方法作用是定义o1,o2的
* 大小规则,返回值关心的是取值范围,当:
* 返回值>0时,表示o1>o2;
* 返回值<0时,表示o1<o2;
* 返回值=0时,表示o1=o2.
*/
@Override
public int compare(Point o1, Point o2) {
int len1 = o1.getX() * o1.getX() + o1.getY() * o1.getY();
int len2 = o2.getX() * o2 .getX() + o2.getY() * o2.getY();
return len1 - len2;
// return len2 - len1;倒序
}
});
System.out.println(list);
// MyComparator c = new MyComparator();
// Collections.sort(list, c);
}
}
//class MyComparator implements Comparator<Point>{
// @Override
// public int compare(Point o1, Point o2) {
// int len1 = o1.getX() * o1.getX() + o1.getY() * o1.getY();
// int len2 = o2.getX() * o2 .getX() + o2.getY() * o2.getY();
// return len1 - len2;
// }
//}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortListDemo3 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("苍井空");
list.add("波多野结衣");
list.add("天海翼");
list.add("王二麻子");
System.out.println(list);
/**
* 当集合元素已经实现了Comparable接口,但是其定义的比较规则不满足
* 我们的排序要求时,我们也可以自行提供一个比较器来完成排序。
*/
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
System.out.println(list);
}
}
Set接口
-
List —— 有序(存储顺序和取出顺序一致),可重复
- Set—— 无序(存储顺序和取出顺序不一致),唯一
我们首先要清楚有序无序:
集合所说的序,是指元素存入集合的顺序,当元素存储顺序和取出顺序一致时就是有序,否则就是无序。
我们一般说的无序是指HashSet,它既不能保证存储和取出顺序一致,更不能保证自然顺序(a-z),而TreeSet 是可以实现自然顺序的。
HashSet
HashSet是如何保证不重复的呢?
通过查看HashSet中add方法的源码,
//HashSet的add源码
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
我们可以看到HashSet中add方法所调用的是HashMap中的put方法,而put方法防止重复考得就是哈希表(哈希表图在Map中)。
TreeSet
TreeSet:底层是二叉树结构(红黑树是一种自平衡的二叉树)
如何存储
那么这一种结构又是如何存储元素的呢?(我们将上图中圆圈称为节点)
-
第一个元素存储的时候,直接作为根节点存储
-
第二个元素开始,每个元素从根节点开始比较
-
若大 则作为右孩子
-
若小 则作为左孩子
-
相等 则不作处理
我们来举一个例子看看:
import java.util.Set;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
Set<Integer> ts = new TreeSet<Integer>();
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
ts.add(17);
ts.add(24);
ts.add(19);
ts.add(18);
ts.add(24);
for (Integer i : ts) {
System.out.print(i + " ");
}
}
}
//运行结果
17 18 19 20 22 23 24
我们使用图片来解释一下上面的代码:
我们将第一个数字20 作为根节点存放,第二个数字18比20小所以放在左边 23大放在右边
例如22这个数字是如何放到如图的位置呢?
首先22先和20比较是大的所以放到右边,接着继续和23进行比较是小的,所以放到23的左边,接下来同理
我们看到运行结果,很神奇的是按照顺序输出的,这也正符合了我们一开始给出的结论:TreeSet 是可以实现自然顺序的
如何取出
那么TreeSet中元素是如何取出来的呢?
从根节点开始,按照左,中,右的原则依次取出元素即可。
分析:我们的根节点是20,所以先看左边也就是18,但是下面还有子节点,我们继续看左边所以第一个数字就是17,然后再看中和右也就是18和19,这时候根节点的左边也就全部看完了,所以接着就是中间的根节点20,右边同理。
Queue接口
Queue
- 队列(Queue)是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。
- 队列遵循先进先出的原则。
- JDK中提供了Queue接口,同时使得LinkedList实现了该接口(选择LinkedList的原因在于Queue经常要进行添加和删除的操作,而LinkedList在这方面效率比较高)。
import java.util.LinkedList;
import java.util.Queue;
public class QueueDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
// offer方法:入队操作,元素会追加到队列末尾
queue.offer("one");
queue.offer("two");
queue.offer("three");
queue.offer("four");
queue.offer("five");
System.out.println(queue);
// 集合的相关方法也可以使用
System.out.println(queue.size());
// poll出队操作,获取队首元素并从队列中删除
String str = queue.poll();
System.out.println(str);
System.out.println(queue);
// peek:引用队首元素,获取后元素还在队列中
str = queue.peek();
System.out.println(str);
System.out.println(queue);
// 遍历队列元素 ,使用迭代器遍历不会影响队列中的元素,遍历完毕后元素还在队列中。
for(String s: queue) {
System.out.println(s);
}
System.out.println(queue);
// 使用poll方法遍历队列(一次性的)
while(queue.size() > 0) {
System.out.println(queue.poll());
}
// int len = queue.size();
// for (int i = 0; i < len ; i++) {
// System.out.println(queue.poll());
// }
System.out.println(queue);
}
}
Deque
- Deque是Queue的子接口,定义了所谓“双端队列”即队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口。
- 双端队列示意图:
import java.util.Deque;
import java.util.LinkedList;
public class DequeDemo {
public static void main(String[] args) {
Deque<String> deque = new LinkedList<String>();
deque.offer("one");
deque.offer("two");
deque.offer("three");
System.out.println(deque);
deque.offerLast("four");
System.out.println(deque);
deque.offerFirst("five");
System.out.println(deque);
String s = deque.poll();
System.out.println(s);
System.out.println(deque);
s = deque.pollFirst();
System.out.println(s);
System.out.println(deque);
s = deque.pollLast();
System.out.println(s);
System.out.println(deque);
}
}
栈
- 如果将Deque限制为只能从一端入队和出队,则可实现“栈”(Stack)的数据结构,对于栈而言,入栈称之为push,出栈成为称之为pop。
- 栈遵循先进后出的原则。
- 通常我们可以使用栈结构实现“后退”这样的功能。
import java.util.Deque;
import java.util.LinkedList;
public class StackDemo {
public static void main(String[] args) {
Deque<String> stack = new LinkedList<String>();
stack.push("one");
stack.push("two");
stack.push("three");
stack.push("four");
System.out.println(stack);
String str = stack.pop();
System.out.println(str);
System.out.println(stack);
for (String s : stack) {
System.out.println(s);
}
}
}
Collection集合应用时的选择
是否唯一
-
唯一:Set
-
需要排序:TreeSet
-
不需要排序:HashSet
-
如果你知道是Set,但是不知道是哪个Set,就用HashSet。
-
不唯一:List
-
需要安全:Vector
-
不需要安全:ArrayList或者LinkedList
-
查询多:ArrayList
-
增删多:LinkedList
如果三原则
如果你知道是List,但是不知道是哪个List,就用ArrayList。
如果你知道是Collection集合,但是不知道使用谁,就用ArrayList。
如果你知道用集合,就用ArrayList。
在集合中常见的数据结构
ArrayXxx:底层数据结构是数组,查询快,增删慢
LinkedXxx:底层数据结构是链表,查询慢,增删快
HashXxx:底层数据结构是哈希表。依赖两个方法:hashCode()和equals()
TreeXxx:底层数据结构是二叉树。两种方式排序:自然排序和比较器排序
Map接口
java.util.map接口 -----查找表
Map是常用的一组数据结构,样子像是一个多行两列的表格。
左列称为key,右列称为value。
Map总是根据key来获取对应的value,并且Map有一个强制的要求,key是不允许重复的(equals比较)。
Map集合和Collection集合的区别?
-
Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的
-
Collection集合存储元素是单独出现的,Collection的子类Set是唯一的,List是可重复的。
-
Map集合的数据结构值针对键有效,跟值无关,Collection集合的数据结构是针对元素有效。
常用实现类:
java.util.HashMap称为散列表或哈希表
散列表是当今查询速度最快的数据结构。
- 底层数据结构是哈希表,线程不安全,效率高。
- 哈希表依赖两个方法:hashCode()和equals()。
-
哈希表
- 只使用hashCode()来判断是否重复可以吗?答案是否定的。
我们给出这样一句话:
对象相等则hashCode一定相等,hashCode相等对象未必相等,只有equals返回true,hashCode才相等。
-
如果类没有重写这两个方法,默认使用的Object()。一般来说不会相同。
-
而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉。只留下一个。
-
对于String 类型来说,不用重写 hashCode()方法和equals()方法都可以保证元素的唯一性,但是如果不是Stirng,而是其它自定义的对象就要重写这两个方法才能保证元素的唯一性。
package map;
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
/**
* V put(K k,V v) 将给定的一组键值存入Map中
* 由于Map要求key不允许重复,因此若使用Map已经存在key存放value,则是替换value操作,
* 调用后返回值为该key被替换掉的value。
* 如果给定的key不存在则返回值为null.
*/
map.put("语文",99);
map.put("数学",98);
map.put("英语",97);
map.put("物理",96);
Integer value = map.put("化学",99);
System.out.println(value);
System.out.println(map);
value = map.put("化学",98);
System.out.println(value);
System.out.println(map);
/**
* V get(Object k)
* 根据给定的key获取对应的value,若给定的key不存在则返回值为null。
*/
value = map.get("语文");
System.out.println(value);
// int v = map.get("体育");不能用int型接收,相当于null.intValue()自动添加开箱方法
// System.out.println(v);会出现空指针异常
/**
* V remove(Object k)
* 从Map中删除给定的key所对应的这组键值对,返回值为该key对应的value。
*/
value = map.remove("数学");
System.out.println(map);
System.out.println(value);
int size = map.size();
System.out.println(size);
map.clear();
System.out.println(map);
}
}
Map的遍历
遍历Map有三种方式:
1.遍历所有的key
2.遍历所有的键值对
3.遍历所有的value(相对不常用)
package map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class MapDemo2 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("语文",90);
map.put("数学",80);
map.put("英语",70);
map.put("物理",60);
map.put("化学",50);
/**
* set keySet()
* 将当前Map中所有的key以一个set集合形式返回,遍历该集合等同于遍历所有的key。
*/
Set<String> keySet = map.keySet();
for(String key:keySet) {
System.out.println("key:" + key);
}
/**
* 遍历每一组键值对
* Set entrySet()
* 将当前Map中每组键值对(若干的Entry实例)以一个Set集合形式返回。
* java.util.Map.entry
* Entry的每一个实例表示Map中的一组键值对。
*/
Set<Entry<String, Integer>> entrySet = map.entrySet();
for (Entry<String, Integer> e : entrySet) {
String key = e.getKey();
Integer value = e.getValue();
System.out.println("key:" + key + ",value:" + value);
}
/**
* Collection values()
* 将当前Map中的所有value以一个集合形式返回
*/
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.println("value:" + value);
}
}
}
- Hashtable和HashMap的区别?
Hashtable:线程安全,效率低。不允许null键和null值。
HashMap:线程不安全,效率高。允许null键和null值。
(其实HashMap就是用来替代Hashtable的,就像ArrayList替代vector一样)
- List,Set,Map等接口是否都继承子Map接口?
List,Set不是继承自Map接口,它们继承自Collection接口。
Map接口本身就是一个顶层接口。
吃得苦中苦,方为人上人!