文章目录
一、Java集合框架
1.集合框架概述
在Java.util包中为Java集合框架我们提供了使用方便的接口和类。
Java集合框架主要提供了如下几种接口和使用类:
(1)单值
操作接口:Collection、List、Set
(2)键值对
操作接口:Map
(3)输出
接口:Iterator、ListLterator
(4)比较
接口:Comparable、Comparator
(5) List接口实现类:ArrayList和LinkedList
(6) Set接口实现类:HashSet和TreeSet
(7) 辅助工具类:Collections类和Arrays类
二、List接口及其实现类
1.List接口
(1)List接口
List接口特点:
(1)有序的:
这里的有序并不是排好了序,而是指元素放进去的顺序和取出来的是一样的。
(2)可重复的:
允许有相同的元素存在。
注意:
接口是不能直接实例化的,如果想要使用List接口,要通过其子类,常用的子类有ArrayList和LinkList。
List集合排序:
在Utilities里面有个Collections和Arrays类,Arrays类里面有个sort方法可以实现对数组元素的排序,在Collections类里面sort同样也可以实现对数组的排序。
方法一:
public static < T extends Comparable <? super T>>void sort(List < T > list, Comparator <?super T>c )
方法二:
public static < T > void sort(List < T > list,Comparator < ? super T > c )
注意:
第一个sort方法传递的是list集合作为参数;第二个sort方法不仅需要传递list集合,而且需要传递一个Comparator这样一个接口类型的对象。
例:使用Collections类存储字符串的list集合进行排序:
public class SortedListDemo2 {
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();//构建了ArrayList类型的对象
persons.add(new Person("小王",30));
persons.add(new Person("张三",10));
persons.add(new Person("李四",20));
for(int i = 0;i < persons.size(); i++) {
System.out.println(persons.get(i));
}
Collections.sort(persons);
System.out.println("forEach方式输出:");
for(Person p : persons) {
System.out.println(p);
}
}
}
//对应的Person类型必须要实现Comparable接口
class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name,int age) {
super();
this.name = name;
this.age = age;
}
public String toString() {
return "Person [name="+name +", age="+ age +"]";
}
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
}
输出结果:
Person [name=小王, age=30]
Person [name=张三, age=10]
Person [name=李四, age=20]
forEach方式输出:
Person [name=张三, age=10]
Person [name=李四, age=20]
Person [name=小王, age=30]
2.List实现类
(1)可变数组-----ArrayList
ArrayList源码:
public class ArrayList< E > extends AbstractList< E >
implements List< E > , RandomAccess , Cloneable , Serializable
ArrayList底层使用数组作为实现结构,但是元素个数不受限制,是
大小可变的数组
,在内存中分配连续的空间,遍历
元素和随机访问
元素的效率比较高,不足之处是任何不在结尾处增加元素或删除元素的操作都会引起大量元素的移位
,这种情况下效率不高。
例:使用ArrayList存储字符串编码:
public class ArrayListDemo1 {
public static void main(String[] args) {
//建立列表,默认元素数为10(在这里面我们构建了一个list对象,这里面使用了泛型,<String>符号就代表我们这里面存放的都是字符串类型的数据,构建好了一个对象后它的默认元素个数为10)
List<String> list = new ArrayList<String>();//首先构建了一个ArrayList类型的对象,注意这里“List<String> list” 声明的是接口类型
System.out.println("添加元素...");
//在末尾添加元素(add方法是实现添加元素的,默认的都会添加到集合的尾部,在尾部追加相应的元素)
list.add("BASIC");
list.add("C");
list.add("Python");
list.add("Java");
System.out.println(list);//此方法是直接输出集合这个对象引用,由于这个集合重写了toString方法,所以说它是可以把里面的内容输出出来的(被当成一个整体来输出的);如果没有重写toString方法,它会输出它所对应的地址值
System.out.println("在第2个元素前面插入");
list.add(2,"C++")
System.out.println(list);
list.set(0,"Ada");//修改 (利用set方法将0位置的这个元素值修改成另外一个元素)
System.out.println(list.get(3));//读取 (调用list.get(3)来获取索引值为3的那个元素的内容)
System.out.println("用下标遍历列表:");
for(int i = 0; i < list.size();i++) {
//通过遍历list集合依次取出了这个集合中的每个元素,而不是当成整体输出
System.out.println(list.get(i));//当没有到达它的长度时就调用它的get方法去传递索引值取出它所对应的元素值
}
}
}
输出结果:
添加元素...
[BASIC, C, Python, Java]
在第2个元素前面插入
[BASIC, C, C++, Python, Java]
Python
用下标遍历列表:
Ada
C
C++
Python
Java
(2)链表-----LinkList
LinkList底层采用链式存储结构,插入、删除元素时不会引起大量元素的移动,效率高。
注意:它还提供了对尾部和头部添加和删除的操作方法!
例:使用LinkList在尾部和添加字符串:
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<String>();//构建了LinkedList类型的对象
System.out.println("添加元素...");
list.add("BADIC");//利用add方法添加了BADIC和C两个元素
list.add("C");
list.addFirst("Python");//LinkedList类特有的方法
list.addLast("Java");//LinkedList类特有的方法
System.out.println("链表头:"+list.getFirst());
System.out.println("链表尾:"+list.getLast());
for(String s:list) {
System.out.println(s);
}
}
}
输出结果:
添加元素...
链表头:Python
链表尾:Java
Python
BADIC
C
Java
三、Set接口及其实现类
1.Set接口
(1)特点
Set与List接口相比,其结构特点如下:
(1)无序的:
即放进去的顺序和出来的顺序不同。
(2)不可重复的:
注重独一无二
的性质。
2.Set实现类
(1)HashSet
特点:
(1) HashSet是根据对象的哈希值
来确定元素在集合中的存储位置,因此具有良好的存储和查找
性能。
(2) HashSet所存储的元素是不可重复的,并且元素都是无序的。(当向 HashSet 集合中添加
一个对象时,首先会调用该对象的hashCode()
方法来计算对象的哈希值
,从而确定元素的存储位置
,如果此时哈希值相同,再调用对象的equals()
方法来确保该位置没有重复元素。
HashSet源码:
public class HashSet< E > extends AbstractList< E >
implements Set< E > Cloneable , Serializable
例:使用HashSet 存储字符串:
public class HashSetDemoTwo {
public static void main(String[] args) {
Set<Person> set = new HashSet<Person>();// 构建了一个HashSet方法,使用的泛型是Person
Person p1 = new Person("小王", 18);
set.add(p1);// 利用add把p1添加进去
Person p2 = new Person("张三", 17);
set.add(p2);
Person p3 = new Person("李四", 19);
set.add(p3);
Person p4 = new Person("李四", 19);// 又初始化一个名字为李四,年龄为19的对象
System.out.println(set.add(p4));// 输出看看有没有添加成功,所以set.add(p4)不仅执行了这个方法,还想把这个结果输出看一下
Person p5 = p2;// 把p2复制给p5,也就意味着p2指向的是 “张三” ,把p2指向的那个地址给了p5,p5也就指向了 “张三”
System.out.println(set.add(p5));// 输出这时候看看p5是否是添加成功
for (Person p : set) {
// 输出这个集合p的所有元素
System.out.println(p);
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public int hashCode(){
//第一步是要重写Person里面的hashCode方法来缩小寻找的成本
return name.hashCode();//既首先保证这两个对象hashCode的值是相同的
}
//第二步:是否真正的找到了对象,还需要去重写equal方法
public boolean equals(Object obj) {
//下面是equals判断
if(obj instanceof Person) {
Person person = (Person)obj;//如果是Person类的话就把它强制转换成对应的person
return this.name.equals(person.name)&&this.age==person.age;//它们两个的属性值全都一样就认为是它们是相等的
}
return false;//另外其它的情况就认为是false
}
public String toString() {
return "Person [name=" + name + ",age=" + age + "]";
}
}
输出结果:
false
false
Person [name=李四,age=19]
Person [name=张三,age=17]
Person [name=小王,age=18
(2)TreeSet
TreeSet是Set的另外一个实现类,它和HashSet很相似,也是不可重复的,但是有一点儿不同,它会一直
保持集合处于有序的状态
。定义如下:
public class TreeSet< E > extends AbstractList< E >
implements NavigableSet< E > ,Cloneable , Serializable
注意:
这里的 NavigableSet是SortedSet的子接口,其定义如下:public interface NavigableSet< E > extends SortedSet< E >
对TreeSet集合进行排序,可以有两种方式:
1. 利用它所对应的类型本身去实现Comparable这样一个可比较的接口。
2. 在构建TreeSet集合对象的时候去传递一个实现了Comparator这样的一个接口的一比较器。
例:使用TreeSet存储自定义对象:
注: 使用TreeSet必须要知道:
集合中的元素必须是实现Comparable接口的类型
或使用带Comparator参数的构造方法来创建TreeSet
,否则编译会通不过。
public class TreeSetDemo2 {
public static void main(String[] args) {
Set<Person> set = new TreeSet<Person>();//首先构建一个TreeSet集合的对象,并且往这个集合里面去添加Person
//希望利用Person把这三个人的年龄按照从小到大的顺序进行输出
Person p1 = new Person("小王",18);
set.add(p1);
Person p2 = new Person("张三",17);
set.add(p2);
Person p3 = new Person("李四",19);
set.add(p3);
for(Person p : set) {
System.out.println(p);
}
}
}
class Person implements Comparable<Person>{
//该类应该相应的实现Comparable接口
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String toString() {
return "Person [name=" + name +",age=" + age + "]";
}
@Override
public int compareTo(Person o) {
return this.age-o.age;//使得年龄从小到大排序
}
}
输出结果:
Person [name=张三,age=17]
Person [name=小王,age=18]
Person [name=李四,age=19]
四、Map接口及其实现类
1.Map接口特点
Map接口特点:
(1) 键(key):不允许重复
(2) 一个键(key) 只能映射到一个值
(value)
2.Map实现类
重点分析HashMap和TreeMap。
(1)HashMap
HashMap主要方法:
例:使用HashMap来存储姓名和电话信息
public class HashMapDemo1 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("zhangsan","15254305006");//姓名和电话号码都是字符串类型的,所以上面指明泛型的时候就需要指明两种类型都是String类型
map.put("zhangsan","15254305066");//如果键相同的时候,它会覆盖原先的值
map.put("lisi", "15254305068");//通过Map里面的put方法往里面去存一对值(键值对),但是Map的键不允许重复
map.put("wangwu","13405786007");
System.out.println(map);//对map里面的元素整体输出,并不是对它里面的元素单个的输出
}
}
输出结果:
{
lisi=15254305068, zhangsan=15254305066, wangwu=13405786007}
(1)HashTable
(1)TreeMap
TreeMap中的元素按照key自动排序,而HashMap是无序的。
例:使用TreeMap存储名字和电话信息。
public class TreeMapDemo {
public static void main(String[] args) {
Map<String,String> map = new TreeMap<String,String>();//构建了TreeMap类型的对象
map.put("zhangsan", "15254305006");//往里面添加了姓名和手机号
map.put("lisi","15254305068");
map.put("wangwu", "13405786007");
System.out.println(map);
}
}
输出结果:
{
lisi=15254305068, wangwu=13405786007, zhangsan=15254305006}
五、泛型
1.案例
为了防止狗堆里混进猫,我们可以采用如下两种方式解决:
方法一: 使用万用字符,修改方法的声明
方法的声明:
private static void takeAnimals(List <? extends Animal
> animals)
public class GenericsDemo3 {
public static void main(String[] args) {
//创建Animal集合
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
takenAnimals(dogs);
}
//所有出现" ? "的这种万用字符的地方是不能执行add方法的操作
public static void takenAnimals(List<? extends Animal> animals) {
//此方法可以传值,也可以遍历,可以访问,但是不能任意的再添加元素
for(Animal animal:animals) {
animal.eat();
}
//animals.add(new Cat());//此方法已经不能执行了!
}
}
abstract class Animal{
public abstract void eat();
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
输出结果:
狗吃骨头
狗吃骨头
狗吃骨头
方法二:
方法的声明:
private static < T extends Animals > void takeAnimals(List < T > animals)
public class GenericsDemo4 {
public static void main(String[] args) {
//创建Dog类型的集合
List<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
takenAnimals(dogs);
}
public static<T extends Animal> void takenAnimals(List<T> animals) {
//注意此处利用方法二修改的代码!!!
for(Animal animal:animals) {
animal.eat();
}
}
}
abstract class Animal{
public abstract void eat();
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
输出结果:
狗吃骨头
狗吃骨头
狗吃骨头