目录
1.1.6 addAll、containsAll和removeAll
3.1.4 containsKey()方法、containsValue()方法、V remove(K k)方法
1.集合框架—Collection
1.1 Collection
1.1.1 List和Set
List集合:
List接口继承自Collection,特点是可以放重复元素,并且有序
Collection是所有集合的顶级接口,规定了所有集合都要具备的功能
集合与数组一样,用于保存一组元素,但是实现类众多(有多重不同的数据结构),并且对于元素的操作都封装成了方法,使用便捷。
Collection下面常见的两个分类(子接口)
java.util.List:线性表,特点是可以存放重复元素,并且有序
java.util.Set:不可重复的集合,并且大部分实现类的无序的
判定元素是否重复的标志是看元素自身的equals比较的结果
1.1.2 集合持有对象的引用
集合中存储的都是引用类型元素,并且集合只保存每个
元素对象的引用,而并非将元素对象本身存入集合。
1.1.3 add方法
/*
boolean add(E e)
向当前集合中添加给定元素,元素成功添加后返回true
*/
Collection c = new ArrayList();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c);
1.1.4 contains方法
boolean contains(E e)
判断当前集合是否包含给定元素
public class CollectionDemo2 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(5,7));
c.add(new Point(3,6));
System.out.println(c);
Point p = new Point(5,7);
/*
contains判断包含时是根据元素equals方法判断的
*/
boolean contains = c.contains(p);
System.out.println("包含:"+contains);
}
}
1.1.5 size、clear、isEmpty
/*
int size()
返回当前元素个数
*/
int size = c.size();
System.out.println("大小:"+size);
/*
boolean isEmpty()
判断当前集合是否为空集,当size =0 时,返回false。
*/
boolean isEmpty = c.isEmpty();
System.out.println("是否为空:"+isEmpty);
c.clear();//清空集合
System.out.println(c);
System.out.println("是否为空集:"+c.size());
1.1.6 addAll、containsAll和removeAll
Boolean addAll(collection c)
将给定的集合元素添加到当前集合元素中(对于Set集合而言,重复的元素不能放进来多次)
Boolean containsAll(Collection c)
判断当前集合是否包含集合中的所有元素
Collection a.removeAll(Collection c);
删除当前集合中给定元素集合的共有元素
public class CollectionDemo5 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("你好");
c1.add("再见");
c1.add("欢迎");
System.out.println("c:"+c1);
Collection c2 = new HashSet();
c2.add("山东省");
c2.add("济南市");
c2.add("再见");
System.out.println("c2:"+c2);
c1.addAll(c2);
System.out.println(c1);
// c2.addAll(c1);//c1中的java不能重复添加到c2中
// System.out.println(c2);
Collection c3 = new ArrayList();
c3.add("济南市");
c3.add("你好");
System.out.println("c3:"+c3);
boolean contains = c1.containsAll(c3);
System.out.println("全包含:"+contains);
/*
删除当前集合中给定元素集合的共有元素
*/
c1.removeAll(c3);
System.out.println("c1:"+c1);
}
}
1.2 Iterator
集合提供了统一的遍历方式,迭代器
Iterator iterator()
该方法会返回一个遍历当前集合元素的迭代器
java.util.Iterator接口
迭代器接口,规定了迭代器遍历集合的相关操作,不同的集合都提供了用于遍历自身元素的迭代器实现类
使用迭代器遍历集合遵循:问,取,删的步骤进行,其中删除元素不是遍历过程中的必要操作
1.2.1 hasNext、next方法
Boolean hasNext()
判断是否还有下一个元素可以遍历(第一遍历时询问的就是第一个,第二次就是询问第二个以此类推)
E next() 获取集合下一个元素
public class IteratorDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("one");
c.add("*");
c.add("two");
c.add("*");
c.add("three");
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);
it.remove();
}
1.2.2 remove方法
在使用迭代器遍历集合时,不能通过集合的remove方法删除集合元素,否则会抛出并发更改异常。我们可以通过迭代器自身提供的remove()方法来删除通过next()迭代出的元素。
c.remove(str);
迭代器的删除方法是在原集合中删除元素。这里需要注意的是,在调用remove方法前必须通过迭代器的next()方法迭代过元素,那么删除的就是这个元素。并且不能再次调用remove方法,除非再次调用next()后方可再次调用。
1.2.3 增强型for
JDK1.5之后退出一个新特性:增强型for循环。可以让我们用相同的语法遍历集合或数组
新循环遍历集合就是迭代器遍历,编译器会将其改为迭代器遍历。
注意:不要在遍历过程中通过集合方法增删元素。
语法:
for(元素类型e:集合或数组){
循环体
}
public class NewForDemo {
public static void main(String[] args) {
String[] arr = {"one","two","three","four","five"};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//新循环是编译器认可的,编译器在编译时会将其改为传统for循环遍历数组
for (String str : arr){
System.out.println(str);
}
/*
泛型 JDK1.5一起推出的一个特性:广泛应用与集合当中,在集合汇总泛型的作用是用来规定集合中的元素类型
*/
Collection<String > c = new ArrayList<>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
// c.add(123);//编译不通过,因为集合定义时规定元素必须是String
System.out.println(c);
/*
新循环遍历集合就是迭代器遍历,编译器会将其改为迭代器遍历。
注意:不要在遍历过程中通过集合方法增删元素。
*/
//获取集合元素时不用再使用Object接收了,编译器会在编译时自动加上造型操作
for (String str : c){
System.out.println(str);
}
}
}
1.3 泛型机制
1.3.1 泛型在集合中的应用
Arayus类的定义中,ES中的E为泛型参数,在创建对象时可以将类型作为参数传递,此时, 类定义所有的E将被替换成传入的参数
2 集合操作—线性表
2.1 List
2.1.1 ArrayList和LinkedList
List接口继承自Collection,特点是可以放重复元素,并且有序
List的常用实现类
java.util.ArraysList:内部使用数组实现,查询速度更好
java.util.LinkedList:内部使用链表实现,增删性能更好
List除了Collection挺高的操作之外,自身还提供了一套基于下标操作的方法。
public class ListDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// List<String> list = new LinkedList<>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
System.out.println(list);
}
}
JDK8之后集合有退出一中遍历元素的方式:forEach方法,基于lambda表达式遍历
foreach是集合自身大方法,所以如果是一个并发安全的集合,这个办法与其他操作时互斥的,可以保证并发安全。
迭代器遍历集合在多线程并发操作时会有问题:如果我们使用一个并发安全的集合,name该集合自身的方法如:add,remove等是能保证并发安全的 ,但是迭代器遍历互斥,因此存在并发安全问题,原来则需要自行维护互斥关系
public class ForeachDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
// for (String str : c){
// system.out.println(str);
// }
c.forEach(e-> System.out.println(e));
}
}
2.1.2 get和set
E get (int index)
获取给定下标出对应的元素
//获取集合中第三个元素
String str = list.get(2);
System.out.println(str);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
E set(int index,E e)
将给定元素设置到指定位置,返回值为该位置原有的元素
set方法也称为替换元素操作
String old = list.set(2,"3");
System.out.println(list);
System.out.println(old);
/*
将list集合反转
*/
for (int i = 0; i < list.size()/2; i++) {
//获取正数位置上的元素
//将其设置到倒数位置上,并返回被置换的元素
//再将原倒数位置元素(被替换的元素)设置到正数位置上
list.set(list.size()-i-1, list.set(i,list.get(list.size()-i-1)));
}
//方法二:
// Collections.reverse(list);
//import java.util.Collections:集合的工具类,有很多操作集合的功能,下面是用于反转集合的
System.out.println(list);
}
}
结果:
[one, two, three, four, five]
[five, four, three, two, one]
2.1.3 插入、删除和获取
E add(int index , String str) 将给定的元素插入指定位置
E remove (int index) 删除并返回指定下标对应的元素
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);
/*
E add(int index , String str)
将给定的元素插入指定位置
*/
list.add(2,"3");
System.out.println(list);
/*
E remove(int index)
删除并返回指定下标对应的元素
*/
String s = list.remove(3);
System.out.println(list);
System.out.println(s);
}
}
List subList ( int start , int end ) 获取当前List集合中指定范围内的子集
public class ListDemo3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
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);
for (int i = 0; i < subList.size(); i++) {
subList.set(i,subList.get(i)*10);
}
System.out.println(subList);
/*
对子集的操作就是对原集合对应的操作
*/
System.out.println(list);
/*
删除List集合中的2~8
*/
list.subList(2,9).clear();
System.out.println(list);
}
}
2.1.4 List转换数组
Collection定义了一个方法toArray,可以将当前集合转换为一个数组
public class CollectionToArrayDemo {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("one");
c.add("two");
c.add("three");
c.add("four");
c.add("five");
System.out.println(c);
// Object[] arr = c.toArray();
String[] arr = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(arr));
}
}
2.1.5 数组转换List
数组的工具类:java.util.Arrays提供了一个静态方法asList,可以将当前数组转换为一个list集合
public class ArrayToListDemo {
public static void main(String[] args) {
String[] arr = {"one","two","three","four","five"};
System.out.println(Arrays.toString(arr));
List<String> list = Arrays.asList(arr);
System.out.println(list);
/*
数组转换的集合,对其操作就是对源数组对应的操作
*/
list.set(1,"2");
System.out.println(list);
System.out.println(Arrays.toString(arr));
//由于数组的定长的,因此这个集合不能做增删这样回改变元素个数的操作,会抛出异常
// list.add("six");
/*
所有集合都支持一个参数为Collection的构造方法,在创建当前集合的同时包含给定集合中的所有元素
*/
List<String> list0 = new ArrayList<>(list);
System.out.println(list0);
list0.add("six");
System.out.println(list0);
}
}
2.2 List排序
2.2.1 Collections.sort方法
集合的工具类:java.util.Collections提供了一个静态方法sort,可以对List几个进行自然排序(从小到大)
public class SortListDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Random ran = new Random();
for (int i = 0; i < 10; i++) {
list.add(ran.nextInt(100));
}
System.out.println(list);
// Collections.sort(list);//从小到大
/*
JDK8以后,List接口上定义了sort方法,用于排序List集合
*/
Collections.sort(list,(o1, o2) -> o2-o1);//从大到小
System.out.println(list);
}
}
2.2.2 Comparable
Collections.sort(List list)方法排序集合时会有一个请求:集合元素必须实现接口:
Comparable接口,否则编译不通过
Comparable英文是可比较的意思,因此实现了这个接口的类是可比的,因为必须重写该接口中定义的比较方法,只有元素之间可以比较大小才能进行排序
如果sort方法不能直接应用就不要使用这个方法了,因为它要求元素必须配合实现接口,这就有了侵入性。
侵入性:当我们调用某个功能时,他反过来要求我们为其他地方的,添加额外代码,这就是侵入性,这不利于程序后期的维护
先实现一个point类作为集合元素测试集合相关操作
public class Point implements Comparable{
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "(" + x + "," + y + ')';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x &&
y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
@Override
public int compareTo(Object o) {
return 0;
}
}
测试类:
public class SortListDemo {
public static void main(String[] args) {
List<Point> list = new ArrayList<Point>();
list.add(new Point(1, 2));
list.add(new Point(5, 4));
list.add(new Point(8, 6));
list.add(new Point(7, 8));
list.add(new Point(3, 10));
System.out.println(list);
Collections.sort(
list,
(o1, o2) ->o1.getX()*o1.getX()+o1.getY()*o1.getY()-
o2.getX()*o2.getX()-o2.getY()*o2.getY()
);
System.out.println(list);
}
}
2.2.3 Comparator
比较器接口Comparator要求必须重写方法:compare
这个方法的作用是定义两个参数的比较大小规则,返回值为比较结果:
当返回值>0时,表示o1比o2大
当返回值<0时,表示o1比o2小
当返回值=0时,表示o1与o2相对
Collection.sort(List list,comparator c)该方法就是利用传入的比较器将集合中两两元素调用比较compare方法,
按照比较结果进行从小到大排序的
Collections.sort(list);
Comparator<Point> c = new Comparator<Point>() {
public int compare(Point o1, Point o2) {
int olen1 = o1.getX()*o1.getX()+o1.getY()*o1.getY();
int olen2 = o2.getX()*o2.getX()-o2.getY()*o2.getY();
return olen1 - olen2;
}
};
Collections.sort(list c);
2.3 队列和栈
2.3.1 Queue
队列 java.util.Queue接口
Queue接口继承自Collection,因此队列一是一个集合,它的实现类可以保存一组元素,但是对于存取元素要求必须遵循先进先出原则
常见实现类:java.util.LinkedList
public class QueueDemo {
public static void main(String[] args) {
Queue<String > queue = new LinkedList<>();
/*
offer方法是入队操作,将元素添加到队列末尾
*/
queue.offer("one");
queue.offer("two");
queue.offer("three");
queue.offer("four");
queue.offer("five");
System.out.println(queue);
/*
E poll()
出队操作,获取元素后,该元素从队列中被删除
*/
String str = queue.poll();
System.out.println(str);
System.out.println(queue);
/*
E 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());
}
System.out.println(queue);
}
}
2.3.2 Deque
java.util.Deque接口,双端队列
Deque继承自Queue,是队列两端都可以作出入队的队列
实现类:java.util.LinkedList
public class DequeDemo {
public static void main(String[] args) {
Deque<String> deque = new LinkedList<>();
deque.offer("one");
deque.offer("two");
deque.offer("three");
System.out.println(deque);
deque.offerFirst("four");
System.out.println(deque);
deque.offerLast("five");
System.out.println(deque);
String str = deque.pollFirst();
System.out.println(str);
System.out.println(deque);
str = deque.pollLast();
System.out.println(str);
System.out.println(deque);
}
}
栈可以保存一组元素,但是存取元素必须遵循先进先出原则,通常使用栈来完成“后退”,“前进”这样的功能。
public class StackDemo {
public static void main(String[] args) {
Deque<String> stack = new LinkedList<>();
stack.push("one");
stack.push("two");
stack.push("three");
stack.push("four");
stack.push("five");
System.out.println(stack);
String str = stack.pop();
System.out.println(str);
System.out.println(stack);
for (String s : stack){
System.out.println(s);
}
System.out.println(stack);
stack.forEach(e-> System.out.println(e));
System.out.println(stack);
}
}
3 集合操作—Map
3.1 Map接口
3.1.1 Map接口
java.util.map查找表
map体现的样子是一个多行两列的表格,左列称为key,右列称为value。Map总是根据key去获取对应的value。
因此我们可以将查询条件作为key,查询结果作为key,查询结果作为value保存到map中一遍将来查找。
map是一个借口,常用实现类:
java.util.HashMap:散列表,使用散列算法实现的map,当今查询速度碎块的数据结构
java.util.TreeMap:使用二叉树实现的map
3.1.2 put()方法
v put(K k , V v) 将给定元素添加到map中
返回值为被替换的value,注:map有一个要求,key不允许重复,所以适用形同的key存放value时会将原来的value值替换掉
此时put方法就将被替换的value返回,否则返回null。
LinkedHashMap是遍历顺序与put顺序一致的HashMap
3.1.3 get()方法
V get(Object key)
根据给定的key获取对应的value,如果给定的key不存在则返回值为null。
//查看语文的分数
value = map.get("语文");
System.out.println("语文:"+value);
value = map.get("数学");
System.out.println(value);
int size = map.size();
System.out.println("size"+size);
3.1.4 containsKey()方法、containsValue()方法、V remove(K k)方法
//是否包含给定的key
boolean ck = map.containsKey("数学");
System.out.println("含有数学:"+ck);
//是否包含给定的value
boolean cv = map.containsValue(98);
System.out.println("成绩是否含有98:"+cv);
/*
V remove(K k)
删除给定的key锁对应的这组键值对,返回值为这个key所对应的value
*/
value = map.remove("语文");
System.out.println(map);
System.out.println(value);
3.2 HashMap
3.2.1 Hash表原理
3.2.2 Hashcode方法
从HashMap的原理中我们可以看到, key的hashCode()方法的返回值对HashMap存储元素时会起着很重要的作用。而hashCode(方法实际上是在Object中定义的。那么应当妥善重写该方法:
对于重写了equals方法的对象般要妥 善的重写继承自Object类的hashCode方法( Object提供的hashCode方法将返回该对象所在内存地址的整数形式)。
重写hashCode方法是需注意两点:其-一、与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法返回值应该相同;其二、hashCode返回的数值应符合hash算法的要求,试想如果有很多对象的hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE (如Eclipse )提供的工具自动生成hashCode方法。
3.2.3 重写hashcode方法
public int hashCode() {
Final int prime = 31;
int result = 1;
result= prime*result+age;
result = prime* result + ((gender == nul)? 0 :gender.hashCode();
result = prime * result + ((name == nul) ?0 :name.hashCode());
long temp;
Temp = Double.doubleToLongBits(salary);
result = prime* result + (int) (temp ^ (temp
>>> 32));
return result;
}
3.2.4 装在因子及HashMap优化
Capacity :容量,hash表里bucket(桶)的数量,也就是散列数组大小。
Initial capacity :初始容量,创建hash表时,初始bucket的数量,默认构建容量是16.也可以使用特定容量。
Size :大小,当前散列表中存储数据的数量。
Load factor :加载因子,默认值0.75(就是75%),当向散列表增加数据时如果size/capacity的值大于Loadfactor则发生扩容并且重新散列(rehash)。
性能优化:加载因子较小时,散列查找性能会提高,同时也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果。在创建散列表时指定合理容量,减少rehash提高性能。
3.3 Map的遍历
map支持三种遍历方式:
1:遍历所有key
2:遍历每一组键值对
3:遍历所有value
3.3.1 使用keySet()方法
遍历所有的key
Set keySet()
将当前Map中所有的key以一个Set集合的形式返回,遍历这个Set集合等于便利了所有的key
Set<String> keySet = map.keySet();
for (String key : keySet){
System.out.println("key:"+key);
}
3.3.2 使用entrySet()方法
遍历每一组键值对
Set(Entry) entrySet()
该方法会将map中每一组键值对以一个Entry实例形式保存到一个Set集合后返回
java.util.Map.Entry
Entry的每个实例表示Map中的每一组键值对,有两个常用方法:getKey和getValue
Set<Map.Entry<String, Integer>> entry = map.entrySet();
for (Map.Entry<String,Integer> e :entry){
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key+" 分数是:"+value);
}
3.3.3 遍历所有的value
Collection<Integer> values = map.values();
for (Integer value : values){
System.out.println("value:"+value);
}
3.4 有序的Map
3.4.1 LinkedHashMap实现有序Map
使用Map接扣的哈希表和链表实现,具有可预知的迭代顺序。
此实现与HashMap的不同之处在于:LinkedHashMap维护着一个双向循环链表。 此链表定义了迭代顺序,该迭代顺序通常就是存放元素的顺序。需要注意的是,如果在Map中重新存入已有的key ,那么key的位置不会发生改变,只是将value值替换。
4 集合综合表