集合框架4

七、Collections工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类。+

Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。

排序操作:(均为static方法)

reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int i, int j):将指定 list 集合中的 i 处元素和 j 处元素进行交换
查找、替换

Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中。这里要注意dest的长度要大于src的长度。一般使用下列语句保证dest的长度和src长度一致,这样才能copy成功。
List dest = Arrays.asList(new Object[list.size()])

boolean replaceAll(List list,Object oldVal,Object newVal)`:使用新值替换List 对象的所有旧值
Collections 类中提供了多个 synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

八、Java泛型

  1. 泛型简介
    所谓泛型,就是允许在定义类、接口时通过 一个标识 表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。

为什么需要泛型?

在泛型出现之前(JDK1.5之前),对于集合容器类,任何类型的数据都可以添加其中,类型不安全。此外在读取集合容器中的对象时,会因为数据类型问题而出现类型转换,一方面类型转换比较繁琐,另一方面可能会在运行时因为类型转换问题出现ClassCastException类型异常。

从JDK1.5以后,Java引入了“参数化类型(Parameterized type)”的概念,允许我们在创建集合时再指定集合元素的类型,正如:List,这表明该List只能保存字符串类型的对象。

JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。

  1. 在集合中使用泛型
    //按照名称从高到低排序,名称一致的时候按年龄的从低到高排
    //使用泛型指定类及接口等容器的数据类型
    import java.util.;
    public class Demo01 {
    public static void main(String[] args) {
    Users u1 = new Users(“harry”,20);
    Users u2 = new Users(“alpha”,28);
    Users u3 = new Users(“beer”,34);
    Users u4 = new Users(“maxc”,25);
    Users u5 = new Users(“sfsdg”,20);
    Users u6 = new Users(“sfsdg”,56);
    //TreeMap map = new TreeMap();
    //使用泛型,限制TreeMap对象中的key为Users类型,value为Integer
    //后面new TreeMap的泛型指定默认和前面一致,JDK7新特性——类型推断
    TreeMap<Users, Integer> map = new TreeMap<>();
    map.put(u1,99);
    map.put(u2,85);
    map.put(u3,77);
    map.put(u4,95);
    map.put(u5,87);
    map.put(u6,77);
    //自然排序
    //Set entrySet = map.entrySet();
    //这里的Set也使用泛型,且这里涉及到了泛型嵌套,Set中的类型都是Map.Entry类型,Entry中的key指定是Users类型,value指定是Iterger类型
    Set<Map.Entry<Users, Integer>> entrySet = map.entrySet();
    //Iterator iterator1 = entrySet.iterator();
    //这里的迭代器也使用泛型,且这里涉及到了泛型嵌套,迭代器中的类型都是Map.Entry类型,Entry中的key指定是Users类型,value指定是Iterger类型
    Iterator<Map.Entry<Users, Integer>> iterator1 = entrySet.iterator();
    while(iterator1.hasNext()) {
    //Set集合entrySet中的每一个元素都是entry对象,即都是一个key-value对,这意味着可以将其转换为Map.Entr类对象
    //Map.Entry类提供了getkey和getvalue方法
    //Object obj = iterator1.next();
    //因为迭代器已经使用泛型指定了类型,因此其中必然是Map.Entry类型的对象,Entry中的key指定是Users类型,value指定是Iterger类型
    Map.Entry<Users,Integer> obj = iterator1.next();
    //Map.Entry entry = (Map.Entry)obj;
    //System.out.println(entry.getKey() + “\t------>\t” + entry.getValue());
    //原本的类型转换也不需要了
    System.out.println(obj.getKey() + “\t------>\t” + obj.getValue());
    }
    }
    }
    //Users类实现了Comparaable接口。Comparale接口也有泛型,并直接影响其中的compareTo方法
    //class Users implements Comparable {
    class Users implements Comparable {
    private String name;
    private double age;
    //按照名称从高到低排序,名称一致的时候按年龄的从低到高排
    @Override
    //由于Comparable接口使用泛型指定了compareTo方法接收参数的类型是Users,因此就不用进行类型判断与转化
    public int compareTo(Users o) {
    int compare = -this.name.compareTo(o.name);
    if (compare != 0) {
    return compare;
    } else {
    return Double.compare(this.age, o.age);
    }
    }
    //也没有类型不一致的异常了
    //throw new RuntimeException(“类型不一致”);
    /

    @Override
    public int compareTo(Object o) {
    //判断元素是都是Users子类
    if (o instanceof Users) {
    //是的话,就强制转换为Users类,类型一致方可比较
    Users users = (Users) o;
    int compare = -this.name.compareTo(users.name);
    if (compare != 0) {
    return compare;
    } else {
    return Double.compare(this.age, users.age);
    }
    }
    throw new RuntimeException(“类型不一致”);
    }
    */
    @Override
    public String toString () {
    return “Users{” +
    “name='” + name + ‘’’ +
    “, age=” + age +
    ‘}’;
    }
    public Users() {
    }
    public Users(String name, double age) {
    this.name = name;
    this.age = age;
    }
    }

运行结果:
Users{name=‘sfsdg’, age=20.0} ------> 87
Users{name=‘sfsdg’, age=56.0} ------> 77
Users{name=‘maxc’, age=25.0} ------> 95
Users{name=‘harry’, age=20.0} ------> 99
Users{name=‘beer’, age=34.0} ------> 77
Users{name=‘alpha’, age=28.0} ------> 85
Java集合框架中的全部接口和类都支持泛型,当未指定类型时,默认是Object类型

  1. 自定义泛型结构
    以往在自定义类、方法、接口时都没有考虑数据类型,现在考虑在自定义类、方法、接口时使用泛型,即自定义泛型类、自定义泛型方法,自定义泛型接口。

3.1 自定义泛型类
//定义一个泛型类,指定类的属性类型为T,T目前不确定,在实例化Box类对象时根据指定类型确定,若未指定默认为Object
class Box {
//创建类的属性t,t的类型为T,目前不确定,在实例化Box类对象时根据指定类型确定,若未指定默认为Object
private T t;
//创建类的成员方法add,add方法接收T类型的参数
public void add(T t) {
this.t = t;
}
//创建类的成员方法get,get方法的返回值的类型为T
public T get() {
return t;
}
}
public class Demo02 {
public static void main(String[] args) {
//实例化泛型类的时候指定泛型参数T的类型
Box integerBox = new Box();
Box stringBox = new Box();
//调用泛型类的方法
integerBox.add(new Integer(10));
stringBox.add(new String(“automan”));
System.out.printf(“整型值为 :%d\n\n”, integerBox.get());
System.out.printf(“字符串为 :%s\n”, stringBox.get());
}
}

运行结果:
整型值为 :10
字符串为 :automan
注意点:

如果定义了泛型类,则实例化的时候就应该指明名类的泛型,否则默认为Object类型

泛型参数T只能是类,不能用基本数据类型填充。但可以使用包装类填充。

使用泛型的主要优点是能够在编译时而不是在运行时检测类型错误。

泛型类可能有多个泛型参数,此时应将多个参数一起放在尖括号内,参数之间用逗号隔开。比如:<E1,E2,E3>

如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象(还是接口和抽象类的特性)。

在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。

静态方法的类型是确定的,但泛型类的类型是在泛型类实例化的时候才确定。因此静态方法不能使用泛型类的泛型参数。

异常类不能是泛型的

泛型不同的引用不能相互赋值。比如上面程序中的integerBox和stringBox不能互相赋值。虽然都是Box类,但是不能像之前一样互相赋值。

3.2 泛型方法
方法也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。泛型方法的泛型参数与泛型方法所在类是否有泛型参数、有哪些泛型参数无关。

泛型方法的语法格式:

[访问权限] <泛型> 返回类型方法名([泛型标识 参数名称]) 抛出的异常

public class Demo03 {
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { ‘H’, ‘E’, ‘L’, ‘L’, ‘O’ };
System.out.println( “整型数组元素为:” );
printArray( intArray ); // 传递一个整型数组
System.out.println( “\n双精度型数组元素为:” );
printArray( doubleArray ); // 传递一个双精度型数组
System.out.println( “\n字符型数组元素为:” );
printArray( charArray ); // 传递一个字符型数组
}
}

运行结果:
整型数组元素为:
1 2 3 4 5
双精度型数组元素为:
1.1 2.2 3.3 4.4
字符型数组元素为:
H E L L O
注意点:

泛型方法可以被声明为静态的,因为泛型方法的泛型参数是在调用方法时确定的,而不是在类实例化的时候确定。
4. 泛型在继承上的体现
4.1 子类与父类的泛型关系
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:

子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留
部分保留
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)擦除父类的泛型
class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{
}
// 子类擦除父类的泛型参数,定义自己的泛型参数A,B
class Son2<A, B> extends Father{//等价于class Son extends Father<Object,Object>{
}
// 2)将父类的泛型参数指定为确定类型
class Son3 extends Father<Integer, String> {
}
// 子类将父类的泛型参数指定为确定类型,定义自己的泛型参数A,B
class Son4<A, B> extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留,与父类的类型参数完全一致
class Son5<T1, T2> extends Father<T1, T2> {
}
// 子类保留父类的泛型参数,同时扩展自己的泛型参数A,B
class Son6<T1, T2, A, B> extends Father<T1, T2> {
}
// 2)部分保留,指定其中部分泛型参数为确定类型,部分泛型参数保留
class Son7 extends Father<Integer, T2> {
}
// 子类指定父类的部分泛型参数为确定类型,同时保留父类的部分泛型参数,同时扩展自己的泛型参数A,B,
class Son8<T2, A, B> extends Father<Integer, T2> {
}
4.2 泛型对继承关系的影响
如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,则G和G不具备父子关系!

比如:String是Object的子类,但是List并不是List的子类,这两个List是并列的,因此不能互相赋值。

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,则A 是B 的父类!,可以赋值。

  1. 通配符的使用
    类型通配符一般是使用?代替具体的类型参数。例如 List<?> 在逻辑上是List,List 等所有List<具体类型实参>的父类。

5.1 通配符简介
如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,则G和G不具备父子关系!它们是并列的,但是它们拥有共同的父类:G<?>

import java.util.*;
public class Demo04 {
public static void main(String[] args) {
//使用带有泛型的ArrayList,创建三个ArrayList对象,将其赋给List型的引用
List name = new ArrayList();
List age = new ArrayList();
List number = new ArrayList();
//根据上面的学习,我们知道这三个ArrayList对象是并列关系,无法互相赋值
name.add(“icon”);
name.add(“berry”);
name.add(“sick”);
name.add(“samm”);
age.add(18);
age.add(24);
age.add(36);
age.add(43);
number.add(314);
number.add(112);
number.add(369);
number.add(257);
getData(name);
getData(age);
getData(number);
}
//要想遍历或者读取三个List中的元素,就要提供各自的方法,这就因为泛型参数而丢失了多态的特性
//使用通配符来重拾多态的特性,getData方法的参数为List<?>类型,这就意味着无论是List、List还是List都可以作为实参 //?就是通配符,即通配所有的类型。这样该方法对三个List都可以使用 public static void getData(List<?> data) {
//注意,这里相当于把name、age、number都有各自的类型转为了List<?>类型 Iterator<?> iterator = data.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println(“*****************”);
}
}

运行结果:
icon
berry
sick
samm
*****************
18
24
36
43
*****************
314
112
369
257
*****************
5.2 使用通配符后的添加与读取
import java.util.*;
public class Demo04 {
public static void main(String[] args) {
//使用带有泛型的ArrayList,创建三个ArrayList对象,将其赋给List型的引用
List name = new ArrayList();
List age = new ArrayList();
List number = new ArrayList();
//根据上面的学习,我们知道这三个ArrayList对象是并列关系,无法互相赋值
name.add(“icon”);
name.add(“berry”);
name.add(“sick”);
name.add(“samm”);
List<?> list = name;
//使用通配符后,将不能再执行add的操作添加数据
//list.add(“automan”)
//使用通配符后还可以执行get操作读取内容,读取的内容类型为Object
System.out.println(list.get(2));
}
}

运行结果:
sick

总结:

使用通配符后,将不能再执行add的操作添加数据
使用通配符后还可以执行get操作读取内容,读取的内容类型为Object
5.3 有限制条件的通配符的使用
通配符指定上限:

上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=

<? extends Number> (无穷小 , Number] 即只允许泛型为Number及Number子类的引用调用 通配符指定下限: 下限super:使用时指定的类型不能小于操作的类,即>= <? super Number> [Number , 无穷大) 只允许泛型为Number及Number父类的引用调用 6. 一个例子 import java.util.*; /* 1.定义个泛型类DAO: 1. 在其中定义一个Map成员变量,Map的键为 String类型,值为T类型。分别创建以下方法: 2. public void save(String id, T entity):保存T类型的对象到Map成员变量中 3. public t get( String id):从map中获取id对应的对象 4. public void update( (String id, T entity):替换map中key为i的内容,改为 entity对象 5. public Listlist():返回map中存放的所有T对象 6. public void delete( String id):删除指定id对象 2.定义一个User类,该类包含: 1.private成员变量(int类型)id,age;(String类型)name。 3.定义一个测试类: 1. 创建DAO类的对象,分别调用其save、get、 update、ist、 delete方法来操作User对象 2. 使用 Junit单元测试类进行测试 */ public class Demo05 { public static void main(String[] args) { Dao userDao = new Dao(); userDao.save("1001",new User(20,2001,"automan")); userDao.save("1002",new User(30,2002,"dsik")); userDao.save("1003",new User(40,2003,"sam")); userDao.save("1004",new User(50,2004,"berry")); List list = userDao.list(); list.forEach(System.out::println); System.out.println("*****************************"); userDao.update("1003",new User(32,2005,"marry")); System.out.println(userDao.list()); System.out.println("*****************************"); userDao.delete("1002"); System.out.println(userDao.list()); System.out.println("*****************************"); System.out.println(userDao.get("1001")); } } class User { private int age; private int id; private String name; @Override public String toString() { return "User{" + "age=" + age + ", id=" + id + ", name='" + name + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return age == user.age && id == user.id && name.equals(user.name); } @Override public int hashCode() { return Objects.hash(age, id, name); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User(int age, int id, String name) { this.age = age; this.id = id; this.name = name; } public User() { } } class Dao { private HashMap
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值