1. java泛型
1. 怎么理解java泛型
-
指定创建的集合中只能存放的类型。
-
泛型是java 1.5引入的新特性,泛型的本质是参数类型化。在类,接口和方法的定义过程中,所操作的数据类型被传入的参数指定。
-
java泛型机制广泛低应用于集合中,所有集合类型都带有泛型, 在创建集合对象的时候,指定集合中元素的类型。
-
java编译器根据泛型的类型,对集合中元素进行类型检查,减少运行的时候错误。
泛型应用场景
- 泛型类
// class 类名<泛型--泛型代表一种类型,相当于是一个参数.类定义的时候,不知道具体的类型.使用类来创建对象的时候,在指定具体类型.
class MyDog<D> {
D d;
public MyDog(D d) {
this.d = d;
}
}
- 使用类型
public static void main(String[] args) {
MyDog<String> m = new MyDog<>("dd");
System.out.println(m.d);
MyDog<D> k = new MyDog<D>(new Date()); // 编译错误 -- 作为一个类型使用的时候,泛型必须指定为某个具体存在的类型.
}
- 泛型接口
interface MyCat<E>{
public E getE();
public void setE(E e);
}
- 类实现接口的时候, 需要指定具体的类型
class AA implements MyCat<String> {
@Override
public String getE() {
return null;
} @
Override
public void setE(String o) {
}
}
- 泛型方法
class BB{
// <T> -- 定义方法的发返回值前,代表方法要使用泛型
public <T> void fun1(T t){
}
public <T> T fun2(T t){
return t;
}
public <T> String fun3(T t){
return "hello";
}
}
- 通配符
// <?> 无界通配符,即不确定类型,可以是任意类型
class CC<Z>{
}
// <? extends T> ,上边界通配符, 即?是继承自T的任意子类型 , 遵守只读不写(只能作为返回
值类型)
class DD<T1 extends CC>{
public void get(T1 t){
}
}
- 使用
public class TPFTest {// 通配符测试
public static void main(String[] args) {
//限制元素是Number或者Number的子类.
List<? extends Number> list1 = new ArrayList<>();
list1.add(null);
// Number的子类有Integer , Double ,编译器没有办法确定add时的子类对象,所以添加不成功
Integer i = 100;
//list1.add( i);编译错误:? extend T 上界类型通配符,只读,不能写. add函数受限制.
Number number = list1.get(0);//可以取出来使用﹒
//限制元素是Intger或其父类.
List<? super Integer> list2 = new ArrayList<>();
list2.add(100);
//Integer object = list2.get(0);//编译错误: ? super T下界类型通配符﹐因为没有办法确定父类是谁,只能写,不能读.
//结论:又要写,又要读,写指定的泛型:
List<Integer> list = new ArrayList<>();
list.add(100);
Integer integer = list.get(0);
}
}
2. Collection(集合)
2.1 List
(有序(添加元素是有顺序的),可以重复)
- List集合的方法——subList
- List的subList方法用于获取子list
- subList获取的子list和原来的list占有相同的存储空间,对subList进行操作会影响原来的list. (删除、修改sublist的元素后,list中的对应位置元素也会被修改)
ArrayList
(动态数组,方便获取元素)
public class ListDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
//给list赋值0~9
for(int i = 0; i < 10; i++) {
list.add(i);
}
//截取子串:,[4,8) -- 包含4, 不包含8
List<Integer> sublist = list.subList(4,8);
System.out.println(sublist);
//把list中[4,8)的元素扩大10倍, 输出list
for(int i = 0; i < sublist.size(); i++) {
sublist.set(i, sublist.get(i)*10);
}
System.out.println("元素扩大10倍:"+list);
//删除
Iterator iter = list.iterator();
int index = 0;
while (iter.hasNext()) {
if(index++ % 2 != 0) { //奇数
iter.next();
iter.remove();
}
if (index >= list.size()) {break;}
}
System.out.println("删除后:"+list);
}
}
LinkedList
(链表,方便插入和删除元素)
// LinkedList中的方法和ArrayList中的方法大部分相同。
public class LinkedListDemo {
public static void main(String[] args) {
// 1. 创建对象
LinkedList<Point> ps = new LinkedList<>(); // ps能调用到LinkedList中的所有方法
List<Point> ps1 = new LinkedList<>(); //ps1能调用到List中的所有方法。
Collection<Point> ps2 = new LinkedList<>();//说明ps2这个引用只能调用到Collection接口中定义的方法
// 2. LinkedList的方法
ps.add(new Point(1, 2)); //增加
ps.add(new Point(2, 3));
ps.add(new Point(3, 4));
Point point = ps.get(2);//根据索引位置找system . out. println(point . x+"-” + point. y );//插入
ps.add(1, new Point(10, 10));
System.out.println(ps);
//插入
ps.add(1, new Point(10, 10));//有序system . out.println(ps ) ;
ps.add(point); //可以重复
System.out.println(ps);
ps.remove(ps.size() - 1);//删除最后一个元素
System.out.println(ps);
//最后一个和第一个交换位置
Point temp = ps.get(0);
ps.set(0, ps.get(ps.size() - 1));
ps.set(ps.size() - 1, temp);
System.out.println(ps);
//循环遍历集合 -- 迭代器
Iterator<Point> iterator = ps.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + "\t");
}
System.out.println();
// lambda -- forEach方法
ps.forEach((one) -> {
System.out.print(one + "\t");
});
//清除集合中的元素
ps.clear();
System.out.println(ps.isEmpty());
}
}
class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
'}';
}
}
2.2 Set集合(无序,不可以重复)
- Set 用于存储不重复的对象集合, set集合中存储的对象,不存在两个对象的equals相等。
- HashSet 和 TreeSet是Set集合的两个常用实现类。
- HashSet 用hash表实现了Set集合。
- TreeSet 用排序二叉树实现了Set集合。
HashSet
- 通过hash算法实现,频率较大
TreeSet
-
Tree的各种算法,频率一般
-
测试:
// Set extends Collectoin
public class SetDemo {
public static void main(String[] args) {
Set<String> sets = new HashSet<String>();
// Set 无序: 添加的元素,在集合中没有顺序
// Set 不重复:
sets.add("A");
sets.add("C");
sets.add("D");
sets.add("B");
sets.add("D");
sets.add("B");
System.out.println(sets);
// 其他方法
System.out.println(sets.size());
sets.addAll(sets);
System.out.println(sets);
sets.remove("A");
System.out.println(sets);
//遍历
for (String s : sets) {
System.out.println(s);
}
sets.forEach(x -> {
System.out.println(x);
});
//简写
sets.forEach(System.out::println);
//练习:在集合中存储20个不重复的100以内的整数
Random r = new Random();
Set<Integer> set = new HashSet<>();
int count = 0;
while (set.size() < 20) {
count++;
set.add(r.nextInt(100));
}
System.out.println("count: " + count);
System.out.println("长度:" + set.size() + " \n" + set);
//产生6个红球。
Set<Integer> redSet = new HashSet<>();
while (redSet.size() < 6) {
redSet.add(r.nextInt(32) + 1);
}
System.out.println("红球:" + redSet);
//练习:数组元素去重
int[] arr = {1, 2, 32, 3, 43, 2, 1};
Set<Integer> newArr = new HashSet<>();
for (int i : arr) {
newArr.add(i);
}
System.out.println("不重复的数组:" + newArr);
}
}
2.3 Map集合(key-value)
Entry:Map中的一个键值对就是一个Entry。
- Map集合主要用于存储“key-value”键值对的数据,key可以看作是value的索引,通过key,查找到value值, key不能重复。
- Map是一个interface , 有多种实现类,比如hash表实现的HashMap ,排序二叉树实现的TreeMap等
- HashMap是Map比较常用的实现类。
- Map接口中常用的get,put方法
- Map测试:
//方法调用
public class MapDemo {
public static void main(String[] args) {
//存储数据:语文-80 ,数学-70,英语-50. ( key-value)
Map<String, Integer> map = new HashMap<>();
//增加数据
map.put("语文", 80);
map.put("语文", 80);
map.put("数学", 70);
map.put("英语", 60);
System.out.println(map); // {数学 = 70,语文 = 80,英语 = 60}//是否包含
System.out.println(map.containsKey("语文")); //true
System.out.println(map.containsValue(70)); //ture
//元素个数
System.out.println(map.size());
map.put("语文", 90); // ** key不存在,则添加,key不存在就修改。
System.out.println(map);
//根据key找到value
System.out.println(map.get("语文"));
System.out.println(map.get("数学"));
//根据key删除集合中的元素
Integer 语文 = map.remove("语文"); // 删除成功的返回值是该key对应的value值
//key的Set集合
Set<String> keys = map.keySet();
System.out.println(keys);
//values的Collection集合
Collection<Integer> values = map.values();
System.out.println(values);
}
}
//Map的遍历:
class MapDemo3 {
public static void main(String[] args) {
//存储数据:语文-80 ,数学-70,英语-50 . ( key-value)
Map<String, Integer> map = new HashMap<>();
//增加数据
map.put("语文", 80);
map.put("数学", 70);
map.put("英语", 60);
map.put("物理", 70);
map.put("化学", 60);
//遍历(把键值对都获取到)
Set<Map.Entry<String, Integer>> entries = map.entrySet(); //把Map转换位Set.
for (Map.Entry<String, Integer> entry : entries) {
// entry就是一个键值对的数
System.out.println(entry.getKey() + "----" + entry.getValue());
}
System.out.println("---------------------");
//根据key ,找到value
Set<String> strings = map.keySet();
for (String key : strings) {
System.out.println(key + "----" + map.get(key));
}
System.out.println("---------------------");
map.forEach((k, v) ->
System.out.println(k + ":" + v)
);
}
}
2.4 集合的遍历
terator
迭代器类:迭代器,遍历集合
- 所有Collection的实现类,都实现了iterator方法, 该方法返回一个Iterator接口类型的对象,用于对于集合中的元素进行迭代遍历。
- Iterator中有三个方法:
boolean hasNext(); // 判断是否还有下一个元素
E next(); // 取出下一个元素
default void remove();//删除元素
增强for循环
- jdk1.5中,推出了增强型for循环的语法,适用于遍历数组和集合中的元素。
2.5 集合的排序(针对有序集合)
Collections
操作集合的工具类(可以通过类名直接使用方法的类)
**Collections类中提供了一些对集合操作的方法,其中比较常用的有对List的排序方法。如果要使用Collections.sort()方法进行对集合中的元素排序,那么要求集合中的元素是实现类Comparable接口的,即对象可以比较大小。 **
Comparable
类实现这个接口,就可以比较大小。
- 针对对象数组或者集合中的元素进行排序的时候,首先需要确定元素的比较逻辑,制定比较的比较规则。
- jdk中的Comparable接口 , 定义了对象间大小比较的方法, 需要大小比较的类,可以实现该接口。
// Comparable -是接口,
//- compareTo (o) , 比较大小
public class ComparableDemo {
public static void main(String[] args) {
String s = "tome";
String s1 = "Jack";
int i = s.compareTo(s1); // i>0表示s大 , i==0,表示相等,i<o ,表示s小.
System.out.println(i); // 42
if (i > 0) {
System.out.println("tom 比 Jack 大");
} else {
System.out.println("tom 比 Jack 小");
}
Student tom = new Student("tom", 19);
Student jack = new Student("jack", 21);
int a = tom.compareTo(jack);
if (a > 0) {
System.out.println("tom大");
} else if (a == 0) {
System.out.println("一样大");
} else {
System.out.println("jack大");
}
//练习:创建5个学生,保存到List中,然后按年龄排序
Student stu1 = new Student("A", 21);
Student stu2 = new Student("B", 20);
Student stu3 = new Student("C", 19);
Student stu4 = new Student("D", 18);
Student stu5 = new Student("E", 17);
List<Student> list = new LinkedList<>();
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
list.add(stu5);
System.out.println("原集合顺序:" + list);
Collections.sort(list); // Collections -- 操作集合的工具类,提供sort方法。
System.out.println("排序后:" + list);
}
}
//判断两个学生的年龄大小
class Student implements Comparable<Student> {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Comparator
函数式接口。排序的时候,通过工具类Collections.sort(集合,Comparator(重写大小比较规则))方法,可以实现这个接口(lambda表达式),然后自定义比较规则。
-
比较规则:
public int compareTo(T o);
返回正数: this > o
返回负数: this < o
返回0 : this == o
public class ComparatorDemo {
public static void main(String[] args) {
List<String> strs = new ArrayList<>();
strs.add("hello");
strs.add(" tom");
strs.add("nice to meet you .");
strs.add("hi");
strs.add("hanmeimei");
Collections.sort(strs); // 采用字符串的默认排序规则: 按字母表顺序
System.out.println(strs);
/* 使用匿名内部类,代码太长
Collections.sort(strs, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
*/
// 使用lambda表达式简化代码
Collections.sort(strs, (o1, o2) -> {
return o1.length() - o2.length();//修改大小比较规则:按字符串长度
});
System.out.println(strs);
}
}
2.6 队列和栈
Queue–队列
- 队列是常用的数据结构,可以将队列堪称是特殊的线性表,队列只能从一端添加元素(offer),另一端取出元素(poll)
- 队列遵循先进先出的原则(FIFO, first in first out)
- Queue是jdk中的接口, LinkedList是Queue的实现类。
- Queue接口中的主要方法:
boolean offer(E e); | 将一个对象添加至队尾,如果添加成功则返回true |
---|---|
E poll0; | 从队首删除并返回一个元素。 |
E peek); | 返回队首的元素(但并不删除)。 |
- 测试:
public class QueueDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
//元素入队
queue.offer("A");
queue.offer("B");
queue.offer("C");
queue.offer("D");
//元素出队
System.out.println("对首元素:" + queue.peek()); //"A"最先进入.所有A是队首元素
//循环出队
while (queue.peek() != null) {
System.out.println(queue.poll()); //出队
}
String poll = queue.poll();//如果队列中没有元素了了.返回值是null.
queue.offer("E");
System.out.println("对首:" + queue.peek());
}
}
Deque–栈
-
Deque是Queue的子接口, 被称为双端队列,即可以从队列的两端入队(offer),出队
(poll),LinkedList实现了该接口。
-
将Deque限定为为只能从一端出队和入队,就可以模拟栈的数据结构,栈数据结构,入栈为push,出栈为pop.
-
栈遵循先进后出的原则FILO(first int last out)
- 测试
// 使用Deque -双端队列模拟出栈的数据结构:控制元素从一端进出.
// -- push(压入) , pop(弹出)
public class StatckDemo {
public static void main(String[] args) {
Deque<String> statck = new LinkedList<>();
statck.push("小A");
statck.push("小A");
statck.push("小A");
statck.push("小A");
//栈最先出的元素:
System.out.println("最先出栈的元素:" + statck.peek());//出栈操作
statck.pop(); //出去小D
statck.pop();//出去小C
System.out.println("即将要出去的元素:" + statck.peek());
statck.push("小E");
statck.push("小F");
System.out.println("即将要出去的元素:" + statck.peek());
statck.pop(); //小F
statck.pop(); //小E
statck.pop(); //小B;
statck.pop(); //小A;
System.out.println("即将要出去的元素:" + statck.peek());
//空栈,继续弹出元素,抛出异常
statck.pop(); // java.util.NoSuchElementException
}
}
3. 练习:
- 定义一个人类(id ,name , 升高, 体重) , 然后创建5个人,添加到集合中, 按bmi指数升序排序。
public class home {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person(10082,"小红",1.59,60));
list.add(new Person(10083,"小绿",1.87,61));
list.add(new Person(10084,"小蓝",1.45,64));
list.add(new Person(10085,"小青",1.63,68));
list.add(new Person(10086,"小紫",1.71,69));
System.out.println("原集合:"+list);
Collections.sort(list);
System.out.println("排序后:"+list);
//输出id, height ,bmi
list.forEach(p->{
System.out.println(p.getId()+"---"+p.getHeight()+"---"+p.getBMI());
});
System.out.println("按身高排序");
Collections.sort(list,(x1, x2)->{
if (x1.getHeight() - x2.getHeight() > 0) {
return 1;
}
if (x1.getHeight() - x2.getHeight() < 0){
return -1;
}
return 0;
});
//输出id, high
list.forEach(p ->{
System.out.println(p.getId() + "---" + p.getHeight());
});
}
}
class Person implements Comparable<Person>{
private int id;
private String name;
private double height;
private double weight;
public Person(int id, String name, double height, double weight) {
this.id = id;
this.name = name;
this.height = height;
this.weight = weight;
}
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double getBMI(){
//BMI即体质指数,是指体重除以身高的平方。
return weight / height * height;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", height=" + height +
", weight=" + weight +
'}';
}
@Override
public int compareTo(Person o) {
if (this.getBMI() - o.getBMI() > 0){
return 1;
}
if (this.getBMI() - o.getBMI() < 0){
return -1;
}
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id; // id相等就表示两个对象相等。
}
@Override
public int hashCode() {
return Objects.hash(id, name, height, weight);
}
}
ArrayList 和 LinkedList区别
- List接口的两个实现类分别用ArrayList , LinkedList 。
- ArrayList & LinkedList都是有序可重复的集合。
- ArrayList用动态数组实现,适合于随机访问
- LinkedList用链表实现,适合于插入和删除。
- ArrayList和LinkedList的方法一样,但是性能不同。