Java API
包括泛型机制简介、泛型的应用、增强型for循环语句、Collection常用方法、集合存放元素的引用(地址)、集合支持泛型、迭代器、ArrayList和LinkedList、数组转换为List集合、List集合转换为数组、List集合排序、List集合常用方法(add、size、set、get、contains、remove、clear、isEmpty、addAll、removeAll、retainAll、subList)、List常用算法(Comparable接口、Collections.sort方法、Comparator接口)、HashSet、Set集合遍历、HashSet和hashCode、hashCode方法。
一、泛型
1、泛型机制简介
(1)Java SE 1.5引入的特性,泛型又称为参数化类型
(2)将当前类的属性的类型,方法参数的类型以及方法返回值的类型的定义权移交给使用者
(3)使用者在创建当前类的同时将泛型的实际类型传入
public class Point<T> { //T为泛型参数
private T x;
private T y;
public Point(T x,T y){
super();
this.x = x;
this.y = y;
}
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
@Override
public String toString() {
return "x="+x+",y="+y;
}
}
2、泛型的应用
(1)泛型的原型是Object,定义了泛型只是编译器在做一些验证工作
(2)当我们对泛型类型设置值时,会检查是否满足类型要求,当我们获取一个泛型类型的值时,会自动进行类型转换
public class TestPoint01 {
public static void main(String[] args) {
/*
指定泛型的实际类型位Integer,但实际上,创建的Point对象中,x、y属性是Object类型,这里只是应当将它当做Integer看待
*/
Point<Integer> p1 = new Point<Integer>(0,0);
/*
由于参数是T,这里会验证实参是否为Integer,若不是,则编译失败!
可以传入基本类型,因为还会自动装箱
*/
p1.setX(1);
//p1.setX(Integer.valueOf(1)); //自动装箱
/*
获取时,也会自动进行造型,这里无需显示的进行类型转换
*/
int x1 = p1.getX();
//int x1 = (Integer)p1.getX(); //自动造型
//int x1 = ((Integer)p1.getX()).intValue(); //自动造型
/*
若不指定泛型,则使用默认的Object类型
*/
Point p2 = p1;
p2.setX("一");
String x2 = (String)p2.getX();
System.out.println("x2:"+x2); //x2:一
//类造型异常!
x1 = p1.getX(); //x1为int型,而p1.getX()获取的是“一”
System.out.println("x1:"+x1); //ClassCastException
}
}
3、增强型for循环语句
1)JDK5.0之后推出一个新的特性:增强for循环,又叫做新循环、for each
2)新循环是经典迭代的“简化版”,新循环不能代替传统循环,作用仅仅是用来遍历集合或数组的
public class ForArrayDemo {
public static void main(String[] args) {
String[] array = {"one","two","three"};
for(int i=0;i<array.length;i++){
String str = array[i];
System.out.print(i+":"+str+" "); //0:one 1:two 2:three
}
System.out.println();
for(String str:array){
System.out.print(str+" "); //one two three
}
}
}
for(String str:array)可以理解为每次循环从数组array中取出一个元素赋值给循环变量str
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class ForCollectionDemo {
public static void main(String[] args) {
Collection c = new ArrayList(); //集合中都是Object类型数据
c.add("one");
c.add("#");
c.add("tuo");
c.add("#");
c.add("three");
System.out.println(c); //[one, #, tuo, #, three]
/*
新循环并非新的语法,新循环是编译器认可,而不是虚拟机认可
使用新循环遍历集合时,编译器会将它改为迭代器方式遍历
所以在使用新循环遍历集合时,不能通过集合的方式增删元素
*/
for(Object o:c){
// Iterator it = c.iterator();
// while (it.hasNext()){
// Object o = it.next();
String str = (String) o;
System.out.print(str+" "); //one # Exception in thread "main" java.util.ConcurrentModificationException
if("#".equals(str)){ //当判断出#和str的值相等时,执行remove会报错ConcurrentModificationException
c.remove(str);
}
}
}
}
二、Collection集合
(1)java.util.Collection集合,用于存储一组元素。提供了维护集合的相关操作
(2)其派生了两个子接口:
-
List:可重复集
-
Set:不可重复集
(3)元素是否重复是依靠元素自身equals方法比较的结果而定的
1、Collection常用方法
方法 | 功能 |
---|---|
int size() | 返回包含对象的个数 |
boolean isEmpty | 返回是否为空 |
boolean contains(Object o) | 判断是否包含指定对象 |
void clear() | 清空集合 |
boolean add(E e) | 向集合中添加对象 |
boolean remove(Object o) | 从集合中删除对象 |
booleac addAll(Collection<? extends E>c) | 将另一个集合中的所有元素添加到集合中 |
boolean removeAll(Collection<?>c) | 删除集合中与另外一个集合中相同的全部元素 |
Iterator<E> iterator() | 返回该集合的对应迭代器 |
2、集合存放元素的引用(地址)
import java.util.ArrayList;
import java.util.Collection;
/**
* 集合存放元素存放的是元素的引用(地址)
*/
public class Test {
public static void main(String[] args) {
Collection c = new ArrayList(); //栈中创建c
Point o = new Point(1,2); //栈中创建o,存在指向堆中元素的地址
System.out.println(o); //结果为(1,2)
c.add(o); //将o添加到c中,栈中c存放的地址指向堆中元素,o为指向的地址信息,所以,c指向的堆元素存放的其实是o的地址信息
System.out.println(c); //结果为[(1,2)]
o.setX(2);
System.out.println(o); //结果为(2,2)
System.out.println(c); //结果为[(2,2)]
}
}
3、集合支持泛型
集合支持泛型,而泛型是用来约束集合中元素的类型
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo01 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<String>();
//只能添加String类型的元素
c.add("one");
c.add("two");
c.add("three");
c.add("four");
//遍历集合元素
for(String str:c){
System.out.print(str+" "); //one two three four
}
System.out.println();
/*
迭代器也应当指定泛型,而泛型的实际类型应当与它遍历的集合的泛型类型一致
*/
Iterator<String> it = c.iterator();
while (it.hasNext()){ //获取元素时不需要再造型了
String str = it.next();
System.out.print(str+" "); //one two three four
}
}
}
4、迭代器
(1)Collection提供了统一的遍历集合元素的方式:迭代器模式
(2)Iterator iterator()获取用于遍历当前集合的迭代器
(3)java.util.Iterator是一个接口,规定了用于遍历集合元素的相关方法,不同的集合提供了相应的实现类,无需记住那些实现类的名字,只将他们当做Iterator即可
(4)遍历集合遵循:问,取,删的步骤,其中删除不是必须操作
(5)Iterator定义了三个方法:
方法 | 功能 |
---|---|
boolean hasNext() | 判断指针后面是否有元素 |
E next() | 获取集合中下一个元素 |
void remove() | 判断是否包含指定对象 |
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add("one");
c.add("#");
c.add("teo");
c.add("#");
c.add("three");
c.add("#");
c.add("four");
//获取用于遍历当前集合的迭代器
Iterator it = c.iterator();
/*
boolean hasNext() 问的过程,该方法是判断集合中是否还有元素可以取出
E next() 取的过程,获取集合中下一个元素
*/
while (it.hasNext()){
String str = (String) it.next(); //需确保集合中存放的元素类型是否一致
if("#".equals(str)){ //当str存在控制null时,#比null,返回的false,如果写成equals(str)."#",当str存在null时,null比“#”会报空指针异常(写法:自变量equals变量)
/*
在使用迭代器遍历集合时,不要使用集合的方法增删元素,否则会引发异常ConcurrentModificationException
*/
//c.remove(str);
/**
* 迭代器提供了remove方法,用来删除next方法取出的元素
* 取一次删一次,删的是取的这个元素
*/
it.remove();
}
System.out.print(str+" "); //one # teo # three # four
}
}
}
对于List集合而言,可以通过基于下标的get()方法进行遍历; 而Iterator方法是针对Collection接口设计,所以实现了Collection接口的类都可以使用Iterator实现迭代遍历
三、List集合
(1)java.util.List可重复集,并且有序。特点是可以根据下标操作元素
(2)List接口是Collection的子接口,用于定义线性表数据结构
(3)List可以理解为存放对象的数组,只不过其元素个数可以动态的增加或减少
1、ArrayList和LinkedList
(1)List接口的两个常见实现类:ArrayList、LinkedList,分别用动态数组和链表的方式实现List接口
(2)ArrayList:使用数字实现,查询更快,适用于随机访问
(3)LinkedList:使用链表实现,增删更快(收尾增删效果明显)
2、数组转换为List集合
(1)使用数组的工具类Arrays的静态方法asList
(2)只能转换为List集合,原因是:Set不能存放重复元素。所以若转换为Set集合可能出现丢失元素的情况
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ArrayToCollectionDemo {
public static void main(String[] args) {
String[] array = {"one","two","three","four"};
List<String> list = Arrays.asList(array);
System.out.println(list); //[one, two, three, four]
/*
向集合中添加一个元素
实际上下面的代码会抛出异常
原因在于该集合是由数组转换过来的,那么该集合就表示原来的数组,所以对集合的操作就是对数组的操作
那么添加元素会导致原数组扩容,那么就不能表示原来的数组了。所以不允许向该集合添加新元素
*/
//list.add("five"); //UnsupportedOperationException
//System.out.println(list);
//修改集合元素,数组元素也会改变
list.set(1,"2");
System.out.println(list); //[one, 2, three, four]
for (String str : array){
System.out.print(str+" "); //one 2 three four
}
System.out.println();
/*
若希望增删元素,需要另外创建一个集合
*/
List<String> list1 = new ArrayList<String>();
list1.addAll(list);
list1.add("five");
System.out.println(list1); //[one, 2, three, four, five]
/*
所有的集合都提供了一个带有Collection类型参数的构造方法
该类构造方法称为:复制构造器
作用是在创建当前集合的同时,集合中包含给定集合中的所有元素
*/
List<String> list2 = new ArrayList<String>(list);
list2.add("five01");
System.out.println(list2); //[one, 2, three, four, five01]
}
}
3、List集合转换为数组
import java.util.ArrayList;
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");
System.out.println(c); //[one, two, three, four]
/*
集合提供了一个方法toArray,可以将当前集合转换为数组
*/
//Object[] array = c.toArray();
String[] array01 = c.toArray(new String[c.size()]);
System.out.println("len:"+array01.length); //len:4
for (String str:c){
System.out.print(str+" "); //one two three four
}
System.out.println();
/*
若给定的数组可用(数组可以存放集合所有元素)是,则使用该数组
若不可用,会自动创建一个与给定数组同类型的数组
*/
String[] array02 = c.toArray(new String[1]);
System.out.println("len:"+array02.length); //len:4
for (String str:c){
System.out.print(str+" "); //one two three four
}
}
}
4、List集合排序
(1)排序集合使用的是集合的工具类Collections的静态方法sort
(2)排序仅能对List集合进行。因为Set部分实现类是无序的
import java.util.*;
public class SortListDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
Random random = new Random();
for(int i=0;i<10;i++){
list.add(random.nextInt(10)); //生成0~99的随机数
}
System.out.println(list); //[8, 2, 1, 6, 4, 7, 5, 8, 8, 4]
/*
对集合进行自然排序,从小到大
*/
Collections.sort(list);
System.out.println(list); //[1, 2, 4, 4, 5, 6, 7, 8, 8, 8]
}
}
5、List集合常用方法
List接口的实现类,实现了Collection中定义的方法
(1)、add
1)void add(E e)方法将对象(的引用)添加到集合中
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list); //[one, two, three, four]
}
}
2)void add(int index,E e)方法将给定元素插入到指定位置
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add(2,"三");
System.out.println(list); //[one, two, 三, three, four]
}
}
(2)、size
size方法返回当前集合中存放对象的数量
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list.size()); //4
}
}
(3)、set
1)E set(int index,E e)将指定元素设置到指定位置上,返回值为原位置得元素
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
String str = list.set(1,"二");
System.out.println(str); //two
System.out.println(list); //[one, 二, three, four]
}
}
2)使用set方法可以实现list中第i个和第j个元素进行交换
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.set(1, list.set(2, list.get(1)));
System.out.println(list); //[one, 二, three, four]
}
}
(4)、get
1)E get(int index)方法获取给定下标对应的元素
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
System.out.println(list.get(3)); //four
}
}
2)循环遍历List集合
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
for(int i=0;i<list.size();i++){
System.out.print(list.get(i)+" "); //one two three four
}
}
}
(5)、contains
boolean contains(E e)方法判断当前集合是否包含给定元素
import day02.ObjectDemo;
import java.util.ArrayList;
import java.util.Collection;
/**
* boolean contains(E e)
* 判断当前集合是否包含给定元素
*/
public class ContainsDemo {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(2,3));
c.add(new Point(3,4));
System.out.println(c); //返回ObjectDemo类中重写的toString方法,如果ObjectDemo类中没重写toString,返回的则是包名@。。。
Point p = new Point(1,2);
//c.add(p);
/*
集合判断是否包含指定元素是依靠元素的equals比较的结果
只要集合中有元素与给定元素比较为true,则认为包含
Point类中重写了equals方法,判断的是内容。如果Point类中没重写了equals方法,则按Object类提供的equals方法进行判断,判断的是指向的地址
*/
boolean b = c.contains(p);
System.out.println("是否包含:"+b); //是否包含:true
}
}
boolean addAll(Collection c)将给定集合中的所有元素添加到当前集合中
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
/**
*
*/
public class CollectionDemo {
public static void main(String[] args) {
Collection c1 = new ArrayList();
c1.add("java");
c1.add("c++");
c1.add(".net");
c1.add("ios");
System.out.println(c1); //[java, c++, .net, ios]
Collection c2 = new HashSet(); //无序,不可重复
c2.add("ios");
c2.add("android");
c2.add("linux");
c2.add("ios"); //返回false
System.out.println(c2); //[android, linux, ios]
/**
* 取并集
* boolean addAll(Collection c)
* 将给定集合中的所有元素添加到当前集合中
* 添加后只要当前集合元素数量发生了变化,则返回true
*/
boolean flag01 = c1.addAll(c2);
System.out.println(c1); //[java, c++, .net, ios, android, linux, ios]
System.out.println(flag01); //true
boolean flag02 = c2.addAll(c1);
System.out.println(c2); //[c++, java, android, linux, .net, ios]
System.out.println(flag02); //true
Collection c3 = new ArrayList();
c3.add("java");
c3.add("android");
//c3.add("windows");
/**
* boolean containsAll(Collection c)
* 判断当前集合是否包含给定集合中的所有元素
*/
boolean contains = c1.containsAll(c3);
System.out.println("是否全包含:"+contains); //是否全包含:true
/**
*从c1集合中删除两个集合中共有的元素
*/
c1.removeAll(c3);
System.out.println(c1); //[c++, .net, ios, linux, ios]
}
}
(6)、remove
E remove(int index)方法从集合中删除指定位置得的元素,并将其返回
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
String str = list.remove(2); //存在返回值,返回的就是删掉的下标对应的值
System.out.println(str); //three
System.out.println(list); //[one, two, four]
}
}
boolean remove(E e)从集合中删除指定元素。删除成功返回true;只删除集合中第一个与给定元素equals比较为true的元素
public class Point<T> { //T为泛型参数
private T x;
private T y;
public Point(T x, T y) {
super();
this.x = x;
this.y = y;
}
public void setX(T x) {
this.x = x;
}
public void setY(T y) {
this.y = y;
}
public T getX() {
return x;
}
public T getY() {
return y;
}
@Override
public String toString() {
return "x=" + x + ",y=" + 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 Objects.equals(x, point.x) && Objects.equals(y, point.y);
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
public class Test {
public static void main(String[] ages) throws ParseException {
Collection c = new ArrayList();
c.add(new Point(1,2));
c.add(new Point(2,3));
c.add(new Point(3,4));
c.add(new Point(1,2));
System.out.println(c);
Point o = new Point(1,2);
c.remove(o); //[x=1,y=2, x=2,y=3, x=3,y=4, x=1,y=2]
System.out.println("删除成功!"); //ObjectDem类重写了equals,所以删的是内容相似的,如果没重写equals,删除的就是栈中地址一致的信息
System.out.println(c); //[x=2,y=3, x=3,y=4, x=1,y=2]
}
}
ArrayList重写的toString方法依次调用了它所包含对象的toString方法,而Point类也重写了它的toString方法
(7)、clear
void clear()方法用于清空集合
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.clear();
System.out.println(list); //[]
}
}
(8)、isEmpty
boolean isEmpty()方法用于返回集合是否为空
public class Test {
public static void main(String[] ages) throws ParseException {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.clear();
System.out.println(list.isEmpty()); //true
}
}
(9)、addAll
addAll方法将另一个集合中所有元素添加进来,“相同”逻辑通过equals方法来判断
public class Test {
public static void main(String[] ages) throws ParseException {
List list1 = new ArrayList();
List list2 = new ArrayList();
list1.add("one");
list1.add("two");
list1.add("three");
list2.add("four");
list2.add("five");
list1.addAll(list2);
System.out.println(list1); //[one, two, three, four, five]
}
}
(10)、removeAll
removeAll方法将删除与另一个集合中所有相同的元素,“相同”逻辑通过equals方法来判断
public class Test {
public static void main(String[] ages) throws ParseException {
List list1 = new ArrayList();
List list3 = new ArrayList();
list1.add("one");
list1.add("two");
list1.add("three");
list3.add("one");
list1.removeAll(list3);
System.out.println(list1); //[two, three]
}
}
(11)、retainAll
retainAll方法将保留与另一个集合中相同的元素,其余的删掉,“相同”逻辑通过equals方法来判断
public class Test {
public static void main(String[] ages) throws ParseException {
List list1 = new ArrayList();
List list2 = new ArrayList();
list1.add("one");
list1.add("two");
list1.add("three");
list2.add("one");
list2.add("two");
list1.retainAll(list2);
System.out.println(list1); //[one, two]
}
}
(12)、subList
List subList(int start,int end)方法获取当前集合中指定范围内的子集。同样含头不含尾
import java.util.ArrayList;
import java.util.List;
/**
* 取子集
* List subList(int start,int end)
* 获取当前集合中指定范围内的子集。同样含头不含尾
*/
public class SubListDemo {
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); //[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
//获取3~7
List<Integer> sublist = list.subList(3,8);
System.out.println(sublist); //[3, 4, 5, 6, 7]
//将子集中每个元素扩大10倍
for (int i=0;i<sublist.size();i++){
int n = sublist.get(i);
n = n * 10;
sublist.set(i,n);
//sublist.set(i,sublist.get(i) * 10); //可以简写成一步
}
System.out.println(sublist); //[30, 40, 50, 60, 70]
/*
对子集的修改,就是修改原集合相应内容
*/
System.out.println(list); //[0, 1, 2, 30, 40, 50, 60, 70, 8, 9]
/*
删除集合2~8中的元素
*/
list.subList(2,8).clear();
System.out.println(list); //[0, 1, 8, 9]
}
}
6、List常用算法
(1)、Comparable接口
1)针对对象数组或者集合中的元素进行排序时,首先要确认对象元素的“比较”逻辑(即那个大,那个小)
2)JDK中定义了Comparable接口,用于表示对象间的大小关系,Java类可以通过实现Comparable接口编写对象的大小逻辑
/**
* 该类用于作为集合的元素
*/
public class Point implements Comparable<Point> {
private int x;
private int y;
Point(int x,int y){
super();
this.x = x;
this.y = y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
return "x="+x+",y="+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);
}
/**
* 当实现了Comparable接口后,需要重写compareTo方法
* 该方法的作用是定义当前对象与给定参数对象比较大小的法则
* 返回值为一个int值,该值表示大小关系。它不关注具体的取值是多少,而关注的是取值范围
* 当返回值>0时:当前对象比参数对象大
* 当返回值<0时:当前对象比参数对象小
* 当返回值=0时:两个对象相等
*/
public int compareTo(Point o) {
/*
坐标的比较规则,点到原点的距离长的大
*/
int len = this.x*this.x + this.y*this.y; //当前对象
int olen = o.x*o.x + o.y*o.y; //给定的参数对象
return len-olen; //不关注具体值,为正则当前对象大,为负返之
}
}
Point实现了Comparable接口,泛型参数指定为Point类型,使得comparaTo方法的参数也为Point类型
在编写comparaTo方法时应该注意和equals方法的一致,即comparaTo判断相等的对象,equals方法应当返回true,反之亦然
(2)、Collections.sort方法
1)java.util.Collections类通过静态方法,提供了对集合的一些实用操作,应用最多的是对List进行排序的sort方法
2)Collections的sort方法要求集合元素(对象)必须实现Comparable接口,从而调用其compareTo方法判断对象的大小
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 排序自定义类型元素的集合
*/
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(8,3));
list.add(new Point(3,4));
list.add(new Point(2,5));
list.add(new Point(2,1));
System.out.println(list);
/*
sort方法要求集合元素必须实现Comparable接口,该接口用于规定实现类是可以比较的
其中有一个方法用来自定义比较大小的规则
*/
Collections.sort(list);
System.out.println(list);
}
}
(3)、Comparator接口
1)我们想使用sort方法排序集合,但是该方法要求我们的集合元素必须实现Comparable接口并且定义比较规则。这种我们想使用某个功能,而它要求我们修改程序的现象称为“侵入性”,修改的代码越多,侵入性越强,越不利于程序的扩展
2)一旦Java类实现了Comparable,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式
3)Comparator接口定义:
import java.util.Comparator;
/**
* 定义一个额外的比较器
*/
class MyComparator implements Comparator<String> {
/**
* 该方法用来定义o1和o2的比较
* 若返回值>0:o1>o2
* 若返回值<0:o1<o2
* 若返回值=0:两个对象相等
*/
public int compare(String o1, String o2) {
/*
字符串中字符多的大
*/
return o1.length()-o2.length();
}
}
比较字符串大小:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortListDemo01 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李湿湿");
list.add("小泽玛利亚");
System.out.println(list);
MyComparator com = new MyComparator();
/*
重载的sort方法要求传入一个额外的比较器
该方法不再要求集合元素必须实现Comparable接口,并且也不再使用集合元素自身的比较规则排序
而是根据给定的这个额外的比较器的比较规则对集合元素进行排序
实际开发中也推荐使用这种方式排序集合元素,若集合元素是自定义的,创建比较器时也推荐使用匿名内部类的形式
*/
Collections.sort(list,com);
System.out.println(list);
/*
Collections有接收Comparator参数的重载sort方法
可以通过匿名类回调的方式调用,此时sort方法将调用Comparator的compare方法判断两个对象的大小关系
匿名内部类形式创建(使用一次,用匿名内部类形式创建)
*/
Comparator<String> com1 = new Comparator<String>() {
public int compare(String o1, String o2) {
return o2.length()-o1.length();
}
};
Collections.sort(list,com1);
System.out.println(list);
}
}
Arrays中针对对象类型数组的sort方法有和Collections的sort方法类似的设计,通过实现Comparable接口或者使用Comparator回调的方式可以对对象数组进行排序
四、Set集合
Set用于存储不重复的对象集合,在Set集合中存储的对象中不存在两个对象equals比较为true
1、HashSet
HashSet和TreeSet是Set集合两个常用的实现类,分别用hash表和排序二叉树的方式实现Set集合
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<Integer> set = new HashSet<Integer>();
Random ran = new Random();
int i=0;
while (set.size()<20){
set.add(ran.nextInt(100)); //将20个随机100以内的不重复整数,放入集合中
i++;
}
System.out.println("循环次数:"+i); //循环次数可能大于20,重复的整数无法放入set集合中
System.out.println(set);
}
}
2、Set集合遍历
(1)Set集合不同于List集合,其中的元素不能和顺序的下标对应,无法从Set集合中取出特定的元素
(2)遍历Set集合中的元素只能调用其iterator方法,通过返回的Iterator对象来完成
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("one");
set.add("four");
set.add("three");
set.add("hundred");
Iterator<String> it = set.iterator();
while (it.hasNext()){
String str = it.next();
System.out.print(str+" "); //hundred four one three
}
System.out.println();
for(String str: set){
System.out.print(str+" "); //hundred four one three
}
}
}
3、HashSet和hashCode
(1)HashSet是Set接口的实现类,通过hash表的方式实现
(2)在将对象加入HashSet集合中时,需要获取对象的hashCode值通过hash算法索引到对应的存储空间
4、hashCode方法
(1)对于重写了equals方法的对象,一般要妥善的重写继承自Object类的hashCode方法(Object提供的hashCode方法将返回该对象所在内存地址的整数形式)
(2)重写hashCode方法需要注意两点:
-
与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法返回值应当相同
-
hashCode返回的数值应符合hash算法的要求,如果有很多对象的hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE(如Eclipse)提供的工具自动生成hashCode方法
public class Point {
private int x;
private int y;
Point(int x,int y){
super();
this.x = x;
this.y = y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
return "x="+x+",y="+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);
}
}
(3)如果不重写Point类的hashCode方法,如下代码返回false,尽管有两个对象的equals为true,但它们的hashCode返回值不相同(为各自的地址),hashSet将不会给这两个对象进行equals比较的机会
import java.util.HashSet;
import java.util.Set;
public class SetDemo01 {
public static void main(String[] args) {
Set<Point> set = new HashSet<Point>();
set.add(new Point(2,3));
set.add(new Point(4,1));
System.out.println(set.contains(new Point(2,3))); //true
}
}