1.容器:数据封装(数组,StringBuilder,对象,集合)
数值很多,用数组存。
数组很多,用二维数组存。
数据很多,用对象存。
对象有很多,有集合存。
数组是固定长度,集合是可变长度。
2.集合:
1.基本内容
1.集合类的由来:
对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。
就使用集合容器进行存储。
2.集合特点:
1.用于存储对象的容器。
2.集合的长度是可变的。
3.集合中不可以存储基本数据类型值。
3.集合容器因为内部的数据结果不同,有多种具体容器。
不断向上抽取,就形成了集合框架。
4.集合框架的顶层是Collection根接口,包含共性内容:
集合在工具包java.util里
1.添加
boolean add(Object obj);
boolean addAll(Collection coll);
2.删除
boolean remove(Object obj);
boolean removeAll(Collection coll); //将两个集合中的相同元素从调用removeAll的集合中删除
void clear(); 清空
3.判断
boolean contains(Object obj); //集合是否包含对象
boolean containsAll(Collection coll); //集合是否包含集合
boolean isEmpty(); 判断集合中是否有元素
4.获取
int size();
Iterator iterator(); 取出元素的方式:迭代器。
该对象必须依赖于具体容器,因为每一个容器的数据结构不同。
所以该迭代器对象是在容器中进行内部实现的。
对于容器使用者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可。
也就是iterator()方法。
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
5.其他:
boolean retainAll(Collection coll); //取两个集合的交集,和removeAll功能相反
Object[] toArray(); 将集合转成数组
2.集合框架的构成图
3.List集合接口:有序
特点:
1.存入和取出的顺序一致,元素都有索引
2.元素可重复
特有的常见方法:
1.添加:
void add(index,element);
void addAll(index, collection);
2.删除:
Object remove(index);
3.修改:
Object set(intdex,element);
4.获取:
Object get(index); //list特有的取出方式之一
int indexOf(Object);
int lastIndexOf(Object);
List subList(from,to);
ListIterator接口:Iterator子接口,存在增删改查方法
解决在使用迭代器操作集合的同时,又在使用集合本身操作集合,而出现的异常。
List list = new ArrayList();
list.add("abc1");
list.add("abc2");
/*
Iterator it = list.lterator();
while(it.hasNext()){
Object obj = it.next();
if(obj.equals("abc2")){
list.add("abc3"); //在迭代器过程中,不用使用集合操作元素,容易出现异常。
}
}*/
ListIterator it = list.listIterator();
//它可以实现在迭代过程中完成对元素的增删改查。
//注意只有list集合具备该迭代功能
while(it.hasNext()){
Object obj = it.next();
if(obj.equals("abc2")){
it.add("abc3"); //正确处理
}
}
/*减少内存写法:
for(Iterator it = coll.iterator();it.hasNext();){
System.out.println(it.next);
}
*/
List接口常用的实现类:Vector, ArrayList, LinkedList
1.Vector: 内部是数组数据结构,是同步的。(数组100%延长,基本不用了)增删,查询都很慢。
2.ArrayList:内部是数组数据结构,是不同步的。替代了Vector。数组50%延长。查询的速度快(空间是连续的)。增删慢(一动而动全身)
3.LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快,查询慢(空间不是连续的)。元素也是有编号的的,只是不连续。
LinkedList:
addFirst();
addLast();
jdk1.6
offerFirst();
offerLast();
getFirst(); //获取但不移除,如果链表为空,抛出NoSuchElementException
getLast();
jdk1.6
peekFirst(); //获取但不移除,如果链表为空,返回Null,可以做判断
peekLast();
removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException
removeLast();
jdk1.6
pollFirst(); //获取并移除,如果链表为空,返回Null,可以做判断
pollLast();
Vector接口中获取元素的方法 :
Enumeration elements();返回一个枚举类型对象。
API:
接口 Enumeration的功能与 Iterator 接口的功能是重复的。此外,Iterator 接口添加了一个可选的移除操作,并使用较短的方法名。新的实现应该优先考虑使用 Iterator 接口而不是 Enumeration 接口。
Vector v = new Vector();
v.addElement("abc1");
v.addElement("abc1");
/*
Enumeration en = v.elements();
while(en.hasMoreElement()){
System.out.println(en.nextElement());
}*/
Iterator it = it.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
LinkedList :
LinkeList link = new LinkeList();
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
System.out.println(link.getFirst()); //获取第一个但不删除
System.out.println(link.removeFirst()); //获取第一个并删除
while(!link.isEmpty()){
System.out.println(link.removeFirst()); //不用迭代器输出对象,但是会删除集合对象。
}
Iterator it = link.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//LinkedList集合练习题:
/*使用LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出 First In Last Out FIFO
队列:先进先出 First In First Out FIFO
应该描述这样一个容器,给使用者提供一个容器对象完成这两种结构中的一种。
*/
//工具类
class DuiLie{
private LinkedList link;
public DuiLie(){
link = new LinkedList();
}
public void myAdd(Object obj){
link.addLast(obj);
}
public Object myGet(){
return link.removeFirst();
}
pubilc boolean isNull(){
return link.isEmpty();
}
}
ArrayList集合存储自定义对象:
public class ArrayListTest {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(new Person("list1",21));//public boolean add(Object obj),所以后面需要强转
al.add(new Person("list2",22));
al.add(new Person("list3",23));
al.add(new Person("list4",24));
Iterator it = al.iterator();
while(it.hasNext()){
// System.out.println(((Person)it.next()).getName()+ " : "+ ((Person) it.next()).getAge());
Person p = (Person) it.next(); //强转
System.out.println(p.getName() + " : " + p.getAge());
}
//al.add(5); //自动装箱
}
}
//ArrayList集合练习题:
ArrayList集合在判断元素是否相同,使用的是元素的equals()方法,只需覆盖equals()方法即可。
而hashSet则需要覆盖hashCode()方法和equals()方法。
数据结构不一样,对元素的判断依据也不一样。
//定义功能去除ArrayList中的重复元素。
//存储字符串的情况:
public static ArrayList getSingleElement(ArrayList al){
//1.定义一个临时容器
ArrayList temp = new ArrayList();
//2.迭代al集合
Iterator it = al.iterator();
while(it.hasNext()){
Object obj = (Object) it.next();
//3.判断被迭代的元素是否在临时容器中存在
if(!temp.contains(obj))
temp.add(obj);
}
return temp;
}
//存储自定义对象的情况:在自定义类中要重写equals()方法
public static ArrayList getSingleElement(ArrayList al){
//1.定义一个临时容器
ArrayList temp = new ArrayList();
//2.迭代al集合
Iterator it = al.iterator();
while(it.hasNext()){
Person p = (Person) it.next();
//3.判断被迭代的元素是否在临时容器中存在
if(!temp.contains(p)) //比较的原理是调用了object的equals方法。
temp.add(p);
}
return temp;
}
public boolean equals(Object obj) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
4.Set集合接口:不重复
特点:
1.元素不能重复
2.无序(有可能“有序”)
set接口中的方法和Collection一致。
|--HashSet:内部数据结构是哈希表(实际上是一个 HashMap 实例) ,是不同步的。
是通过对象的hashcode和equals方法来判断对象唯一性。
如果对象的hashcode值不同,那么不用判断equals方法,直接存储到哈希表中。
如果对象的hashcode值相同,那么要再次判断对象的equals方法是否为true。
如果为true,视为相同元素,不存。如果为false,视为不同元素,就进行存储。
记住,如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。
一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals方法,hashcode方法,建立对象判断是否相同的依据。
|--TreeSet:可以对set集合中的元素进行排序。是不同步的。
判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。
①TreeSet对元素进行排序的方式一:
让元素自身具备比较功能,元素需要实现comparable接口。覆盖compareTo方法。
如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。
②可以使用TreeSet集合的第二种排序方式二:
让集合自身具备比较功能。在集合创建对象时完成。定义一个类实现Comparator接口,覆盖compare方法。将该类的对象作为参数传递给TreeSet集合的构造函数。
TreeSet集合内部数据结构:二叉树(红黑树)(每次插入元素前都会默认记录折中元素的位置,采用二分查找提高效率)
HashSet集合存储自定义对象:
public class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("list1",24));
hs.add(new Person("list2",25));
hs.add(new Person("list3",26));
hs.add(new Person("list2",25));
Iterator it = hs.iterator();
while(it.hasNext()){
Person p = (Person) it.next();
System.out.println(p.getName()+" : "+ p.getAge());
}
}
}
public class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
// System.out.println(this+ ".......hashcode");
return name.hashCode() + age*38; //减少哈希值相同概率
}
@Override
public boolean equals(Object obj) {
// System.out.println(this+ "....equals..." + obj);
if(this == obj)
return ture;
if(obj instanceof Person)
throw new ClassCastException("类型错误");
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString()
{
return name +": "+age;
}
}
LinkedHashSet:由哈希表和链表实现。有序,怎么存怎么取。(注意和排序区别)。
所以判断用什么集合,无关有无顺序。需要唯一性用set集合,不需要就用list集合。
HashSet hs = new LinkedHashSet();
hs.add("abc1");
hs.add("abc2");
hs.add("abc3");
Iterator it = hs.iterator();
while(it.hasNext()){
System.out.println(it.next()); //顺序输出
}
TreeSet集合对元素进行排序的方式一:存储自定义对象时需要实现 Comparable接口,复写compareTo()方法。
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Person> ts =new TreeSet<Person>();
ts.add(new Person("zhangsan",28));
ts.add(new Person("wangwu",21));
ts.add(new Person("lisi",22));
ts.add(new Person("zhaoliu",21));
Iterator<Person> it = ts.iterator();
while(it.hasNext()){
Person p =it.next();
System.out.println(p.getName()+":"+ p.getAge());
}
// demo1(ts);
}
public static void demo1(TreeSet ts) {
ts.add("abc"); //字符串本身就重写了compareTo方法。
ts.add("zaa");
ts.add("aa");
ts.add("nba");
ts.add("dwe");
Iterator it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
public class Person implements Comparable<Person>{ //实现Comparable接口
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person p) { //重写compareTo方法。按年龄排序
// Person p = (Person)o; //元素比较传入一个参数
int temp = this.age - p.age;
return temp==0?this.name.compareTo(p.name):temp;
}
}
比较器Comparator:更常用
(如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。)
TreeSet集合对元素进行排序的方式二:让集合自身具备比较功能。在集合创建对象时完成。
定义一个类实现Comparator接口,覆盖compare方法。将该类的对象作为参数传递给TreeSet集合的构造函数。
TreeSet有序化:如果compare返回值固定是正数,则会按存入顺序输出,如果是负数,则逆序输出。
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts =new TreeSet(new ComparatorByName()); //使用比较器自定义排序功能
ts.add(new Person("zhangsan",28));
ts.add(new Person("wangwu",21));
ts.add(new Person("lisi",22));
ts.add(new Person("zhaoliu",21));
Iterator it = ts.iterator();
while(it.hasNext()){
Person p =(Person) it.next();
System.out.println(p.getName()+":"+ p.getAge());
}
}
public class ComparatorByName implements Comparator {
@Override
public int compare(Object o1, Object o2) { //集合自身比较需要传2个参数
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int temp = p1.getName().compareTo(p2.getName());
return temp ==0?p1.getAge() - p2.getAge():temp;
}
}
3.Map集合:和Collection一样的顶层接口。
Map:一次添加一对元素。Collection一次添加一个元素。
Map也称为双列集合,Collection称为单列集合。
其实Map集合中存储的就是键值对。
Map集合中必须保证 键 的唯一性。
常用方法:
1.添加
value put(key, value); 返回前一个和key关联的值,如果没有返回null
2.删除
void clear(); 清空map集合
value remove(key); 根据指定的键删除这个键值对
3.判断
boolean containKey(key);
boolean containValue(value);
boolean isEmpty();
4.获取
value get(key);通过键获取值。如果没有该键返回null。
当然可以通过返回null,来判断是否包含指定键。
int size(); 获取键值对的个数。
取出map中的所有元素:
①keySet()方法:返回所包含的键的set视图。返回set集合。
public class MapDemo {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<Integer,String>();
method(map);
}
public static void method(Map<Integer,String> map){
map.put(8, "wangwu");
map.put(3, "lisan");
map.put(2, "sunhao");
map.put(6, "tiantian");
//取出map中的所有元素
//原理:通过keyset方法获取map中所有的键所在的set集合,再通过set的迭代器获取到每一个键,
//再对每一个键通过map集合的get方法获取其对应的值即可
Set<Integer> keySet = map.keySet();
Iterator<Integer> it = keySet.iterator();
while(it.hasNext()){
Integer key = it.next();
String value = map.get(key);
System.out.println(key+":"+value);
}
}
}
②entrySet()方法:返回所包含的映射关系的set视图。返回set集合。
/*
通过Map转换成set就可以迭代。
通过entrySet方法 将键和值的映射关系作为对象存储到了Set集合中,而这个映射关系的类型就是Map.Entry类型。
*/
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer, String>> it = entrySet.iterator();
while(it.hasNext()){
Map.Entry<Integer, String> me = it.next();
System.out.println(me.getKey()+":"+me.getValue());
}
③values():返回此映射中包含的值的 Collection 视图。
Collection<String> values = map.values();
Iterator<String> it = values.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
Map集合的常见子类对象:1.0时单列集合就是Vector,双列集合就是Hashtable。
1.Hashtable:内部机构是哈希表,是同步的。不允许null作为键和值。
类Properties:用来存储键值对型的配置文件的信息。可以和IO技术相结合。
2.HashMap:内部结构是哈希表,是不同步的。允许null作为键和值。
3.TreeMap:内部结构是二叉树,是不同步的。可以对Map集合中的键进行排序。
LinkedHashMap:由哈希表和链表实现。有序,怎么存怎么取。(注意和排序区别)。
Map集合练习:
/*
* 练习:
* "fdgavcbsacdfs"获取该字符串中吗,每一个字母出现的次数。
* 要求打印的结果是:a(2)b(1)...
*
* 对于结果的分析发现,字母和次数之间存在映射关系,而且这种关系很多。
* 很多就需要存储,能存储映射关系的容器有数组和Map集合。
*
* 关系中没有一方是有序编号,所以用Map集合。
* 因为保证唯一性的一方具有顺序a,b...,所以使用TreeMap集合。
*
*这个集合最终存储的是字母和次数的对应关系。
*
*1.因为操作的是字符串中的字母,所以先将字符串变成字符数组。
*2.遍历字符数组,用每一个字母作为键去查Map集合这个表。
*如果该字母键不存在,就将该字母作为键,1作为值存储到map集合中。
*如果该字母键存在,就将该字母键的值取出并+1,再将该字母和+1后的值存储到map集合中。键相同值会覆盖。
*3.遍历结束,map集合就记录了所有字母的次数。
*/
public class Test {
public static void main(String[] args) {
String str = "f --2dgavcbsacdfs";
String s = getCharCount(str);
System.out.println(s);
}
public static String getCharCount(String str){
//将字符串变成字符数组
char[] chs = str.toCharArray();
//定义map集合表
Map<Character,Integer> map = new TreeMap<Character, Integer>();
for (int i = 0; i < chs.length; i++) {
if(!(chs[i]>='a' && chs[i] <='z' || chs[i]>='A' && chs[i] <='Z'))
continue;
//将数组中的字符作为键去查map表。
Integer value = map.get(chs[i]);
int count = 1; //简化代码
//判断值是否为null
if(value!=null)
{
count = value + 1;
}
map.put(chs[i],count);
/*
if(value ==null){
map.put(chs[i],1);
}
else{
map.put(chs[i], value+1);
}*/
}
//return map.toString();
return mapToString(map);
}
private static String mapToString(Map<Character, Integer> map) {
StringBuilder sb = new StringBuilder();
Iterator<Character> it = map.keySet().iterator();
while(it.hasNext()){
Character key = it.next();
Integer value = map.get(key);
sb.append(key +"("+ value +")");
}
return sb.toString();
}
}
Map在有映射关系时,可以优先考虑。
在查表法中的应用较为多见。
4.泛型:jdk1.5出现的安全机制。编译时期的安全技术。
好处:
1.将运行时期的问题ClassCastException转到了编译时期。
2.避免了强制转换的麻烦。
<>:
什么时候用:当操作的引用数据类型不确定的时候,就是用<>。将操作的引用数据类型传入即可。
其实<>就是一个用于接收具体引用数据类型的参数范围。
在程序中,只要用到了带有<>的类或接口,就要明确传入的具体引用数据类型。
泛型的 擦除和补偿:
泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。
擦除:运行时,会将泛型去掉,生成的class文件中是不带泛型的,这个称为泛型的擦除。
为什么擦除:为了兼容运行的类加载器。
补偿:在运行时,通过获取元素的类型进行转换动作。不用使用者再强制转换。
泛型在集合中的应用:
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Person> ts =new TreeSet<Person>();
ts.add(new Person("zhangsan",28));
ts.add(new Person("wangwu",21));
ts.add(new Person("lisi",22));
ts.add(new Person("zhaoliu",21));
Iterator<Person> it = ts.iterator();
while(it.hasNext()){
Person p =it.next();
System.out.println(p.getName()+":"+ p.getAge());
}
}
public class Person implements Comparable<Person>{ //实现Comparable接口
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int compareTo(Person p) { //重写compareTo方法。按年龄排序
// Person p = (Person)o; //元素比较传入一个参数
int temp = this.age - p.age;
return temp==0?this.name.compareTo(p.name):temp;
}
}
泛型类:
/*
*在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。
*泛型类,什么时候用:当类中的操作的引用数据类型不确定的时候,就使用泛型类表示。
*
*/
public class Tool<Q>{
private Q q;
public Q getObject() {
return q;
}
public void setObject(Q object) {
this.q = object;
}
}
/*public class Tool {
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}*/
public class GenericDefineDemo3 {
public static void main(String[] args) {
Tool<Student> tool = new Tool<Student>(); //使用泛型类
tool.setObject(new Student()); //如果传入的对象不对,编译报错
Student stu = tool.getObject();
/* Tool tool = new Tool();
tool.setObject(new Worker());
Student stu = (Student)tool.getObject();*/
}
}
泛型方法:<>放在修饰符的后面,返回值的前面。
一旦使用了泛型,只能使用Object的方法。
//将泛型定义在方法上
public <W> void show(W str){
System.out.println("show:" + str);
}
public <Q>void print(Q str){
System.out.println("print:"+ str);
}
/*
//当方法静态时,不能访问 类 上定义的泛型,
//如果静态方法使用泛型,只能将泛型定义在方法上。
public static void method(Q str){
System.out.println("method:"+ str);
}
//正确的写法:
public static<Y> void method(Y str){
System.out.println("method:"+ str);
}
*/
public static void main(String[] args) {
Tool<String> tool = new Tool<String>();
tool.show("abc"); //自动选择参数类型
tool.print(new Integer(4)); //报错,print方法是根据Tool类的泛型来确定
}
泛型接口:
interface Inter<T>{
public void show(T t);
}
class InterImpl implements Inter<String>{
public void show(String str){
System.out.println("show :" + str);
}
}
class InterImpl2 implements Inter<Q>{
public void show(Q q){
System.out.println("show :" + q);
}
}
public static void main(String[] args){
InterImpl in = new InterImpl();
in.show("haha");
InterImpl2<Integer> in2 = new InterImpl2<Integer>();
in2.shoe(5);
}
泛型的高级应用:泛型的限定
泛型的通配符:?未知类型
public class GenericDemo4 {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc");
al.add("hehe");
ArrayList<Integer> al2 = new ArrayList<Integer>();
al2.add(55);
al2.add(67);
printCollection(al);
printCollection(al2);
}
/**
* 迭代并打印集合中的元素
* @param al
*/
//泛型的通配符?
private static void printCollection(Collection<?> al) {
Iterator<?> it = al.iterator();
while(it.hasNext()){
// T t = it.next();
System.out.println(it.next().toString());
}
}
/*
private static <T> void printCollection(Collection<T> al) {
Iterator<T> it = al.iterator();
while(it.hasNext()){
T t = it.next();
System.out.println(t.toString());
}
}*/
}
泛型的限定:? ( 就是 ? extends Object)
/*
?extends E :接收E类型或者E的子类型对象。上限。
?super E: 接收E类型或者E类型的父类型。下限。
*/
private static void printCollection(Collection<? extends Person> al) {
Iterator<? extends Person> it = al.iterator();
while(it.hasNext()){
// T t = it.next();
Person p = it.next();
System.out.println(p.toString());
}
}
泛型限定在集合中的应用:
上限的体现:Collection中的addAll()方法。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
原因:如果不限定,则所有的对象都可以添加,这时在取出的时候需要强制转换,容易出现问题。
一般在存储元素的时候,都使用上限。因为这样取出都是按照上限类型来运算的,不会出现类型安全隐患。
下限的体现:TreeSet中的构造方法:
TreeSet(Comparator<? super E> comparator)
构造一个新的空 TreeSet,它根据指定比较器进行排序。
通常对集合中的元素进行取出操作时,可以用下限。用得较少。
通配符?的体现:Collection中的containsAll()方法。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
因为equals方法比较两边可以是任意对象。
5.集合查阅的技巧:
需要唯一么?
需要:Set
需要指定顺序么?
需要:TreeSet
不需要:HashSet
但是想要一个和存储一致的顺序:LinkedHashSet
不需要:List
需要频繁增删么?
需要:LinkedList
不需要:ArrayList
如何记住每一个容器的结果和所属体系?
后缀名就是该集合所属的体系:
List:
ArrayList
LinkedList
Set:
HashSet
TreeSet
前缀名就是该集合的数据结构:
Array:就要想到数组,想到查询快,有角标
Link:就要想到链表,想到增删快,想到add/get/remove + first/last的方法
Hash:就要想到哈希表,想到唯一性,想到元素需要覆盖hashcode方法和equals方法。
tree:就要想到二叉树,想到排序,想到两个接口Comparable,Comparator。
通常这些常用的集合都是不同步的。
6.集合框架工具类:
Collections: 操作集合的工具类
1.排序:
/*
static <T extends Comparable<? super T>> void sort(List<T> list)
根据元素的自然顺序 对指定列表按升序进行排序。
static <T> void sort(List<T> list, Comparator<? super T> c)
根据指定比较器产生的顺序对指定列表进行排序。
static void swap(List<?> list, int i, int j)
在指定列表的指定位置处交换元素。
*/
public class CollectionsDemo {
public static void main(String[] args) {
/*
* Collections:是集合框架的工具类。
* 里面的方法都是静态的。
*/
demo_1();
}
public static void demo_1(){
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("cba");
list.add("aa");
list.add("zzz");
list.add("cba"); //有重复的,不能用TreeSet
list.add("nbaa");
System.out.println(list);
//对list集合进行指定顺序的排序。
Collections.sort(list);
System.out.println(list);
Collections.sort(list,new ComparatorByLength());
System.out.println(list);
//源码实现:
mySort(list);
System.out.println(list);
mySort1(list,new ComparatorByLength());
System.out.println(list);
}
public static <T> void mySort1(List<T> list, Comparator<? super T> comp){
for (int i = 0; i < list.size()-1; i++) {
for (int j = i+1; j < list.size(); j++) {
if(comp.compare(list.get(i), list.get(j))>0){
// T temp = list.get(i);
// list.set(i, list.get(j));
// list.set(j, temp);
Collections.swap(list, i, j);
}
}
}
}
public static<T extends Comparable<? super T>> void mySort(List<T > list){
for (int i = 0; i < list.size()-1; i++) {
for (int j = i+1; j < list.size(); j++) {
if(list.get(i).compareTo(list.get(j))>0){
// T temp = list.get(i);
// list.set(i, list.get(j));
// list.set(j, temp);
Collections.swap(list, i, j);
}
}
}
}
}
2.折半,最值,逆序,替换,置换,集合和枚举的转换,非同步集合转同步集合。
折半: 先有序化,然后查找。根据有序化分为两种方式:
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 需要自己先排序
使用二分搜索法搜索指定列表,以获得指定对象。
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
使用二分搜索法搜索指定列表,以获得指定对象。
最值:
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 不需要自己排序
根据元素的自然顺序,返回给定 collection 的最大元素。
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
根据指定比较器产生的顺序,返回给定 collection 的最大元素。
逆序:
static <T> Comparator<T> reverseOrder()
返回一个比较器,它强行逆转实现了 Comparable 接口的对象 collection 的自然顺序。
static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
返回一个比较器,它强行逆转指定比较器的顺序。
static void reverse(List<?> list)
反转指定List列表中元素的顺序。
例子:TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder()); //String自己实现了Comparable 接口
原理:TreeSet<String> ts = new TreeSet<String>(new Comparator<String>{
public int compare(String o1,String o2){
int temp = o2.compareTo(o2);
return temp;
}
});
替换:
static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
使用另一个值替换列表中出现的所有某一指定值。
static <T> void fill(List<? super T> list, T obj)
使用指定元素替换指定列表中的所有元素。
置换:
static void shuffle(List<?> list)
使用默认随机源对指定列表进行置换。
static void shuffle(List<?> list, Random rnd)
使用指定的随机源对指定列表进行置换。
集合和枚举的转换:
static <T> ArrayList<T> list(Enumeration<T> e)
返回一个数组列表,它按返回顺序包含指定枚举返回的元素。
static <T> Enumeration<T> enumeration(Collection<T> c)
返回一个指定 collection 上的枚举。
非同步集合转同步集合:
static <T> List<T> synchronizedList(List<T> list)
返回指定列表支持的同步(线程安全的)列表。
static <T> Set<T> synchronizedSet(Set<T> s)
返回指定 set 支持的同步(线程安全的)set。
static <T> Collection<T> synchronizedCollection(Collection<T> c)
返回指定 collection 支持的同步(线程安全的)collection。
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
返回由指定映射支持的同步(线程安全的)映射。
给非同步的集合加锁 原理:
List list = new ArrayList(); //非同步的
list = MyCollection.synList(list); //返回一个同步的list
class MyCollection{
public List synList(List list){
return new MyList(list);
}
private class MyList implements List{
private List list;
private static final Object lock = new Object();
MyList(List list){
this.list = list;
}
public boolean add(Object obj){
synchronized(lock){
return list.add(obj);
}
}
public boolean remove(Object obj){
synchronized(lock){
return list.remove(obj);
}
}
}
}
Arrays:操作数组的集合工具类
1.折半:包括byte,char,double,float,int,short,long,Object,T泛型数组。
static int binarySearch(byte[] a, byte key)
使用二分搜索法来搜索指定的 byte 型数组,以获得指定的值。
static int binarySearch(byte[] a, int fromIndex, int toIndex, byte key)
使用二分搜索法来搜索指定的 byte 型数组的范围,以获得指定的值。
...
static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
使用二分搜索法来搜索指定数组,以获得指定对象。
static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c)
使用二分搜索法来搜索指定数组的范围,以获得指定对象。
2.复制指定数组:包括boolean,byte,char,double,float,int,short,long,T泛型数组。
static boolean[] copyOf(boolean[] original, int newLength)
复制指定的数组,截取或用 false 填充(如有必要),以使副本具有指定的长度。
3.复制指定范围指定数组:包括boolean,byte,char,double,float,int,short,long,T泛型数组。
static boolean[] copyOfRange(boolean[] original, int from, int to)
将指定数组的指定范围复制到一个新数组。
4.判断两个数组是否相等(是指内容全部相同):包括boolean,byte,char,double,float,int,short,long,Object数组。
static boolean equals(boolean[] a, boolean[] a2)
如果两个指定的 boolean 型数组彼此相等,则返回 true。
5.重置:包括boolean,byte,char,double,float,int,short,long,Object数组。
static void fill(boolean[] a, boolean val)
将指定的 boolean 值分配给指定 boolean 型数组的每个元素。
static void fill(boolean[] a, int fromIndex, int toIndex, boolean val)
将指定的 boolean 值分配给指定 boolean 型数组指定范围中的每个元素。
6.哈希码:包括boolean,byte,char,double,float,int,short,long,Object数组。
static int hashCode(boolean[] a)
基于指定数组的内容返回哈希码。
7.排序:包括boolean,byte,char,double,float,int,short,long,Object,T泛型数组。
static void sort(byte[] a)
对指定的 byte 型数组按数字升序进行排序。
static void sort(byte[] a, int fromIndex, int toIndex)
对指定 byte 型数组的指定范围按数字升序进行排序。
...
static <T> void sort(T[] a, Comparator<? super T> c)
根据指定比较器产生的顺序对指定对象数组进行排序。
static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)
根据指定比较器产生的顺序对指定对象数组的指定范围进行排序。
8.字符串表示:包括boolean,byte,char,double,float,int,short,long,Object数组。
static String toString(boolean[] a)
返回指定数组内容的字符串表示形式。
原理:
public static void main(String[] args) {
/*
* Arrays:集合框架的工具类。里面的方法都是静态的。
*/
int[] arr = {3,1,2,5,6,3};
System.out.println(Arrays.toString(arr));
}
//toString的经典实现。
public static String myToString(int[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) { //中间省略判断,提高了效率
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
9.其他:
static boolean deepEquals(Object[] a1, Object[] a2)
如果两个指定数组彼此是深层相等 的,则返回 true。除了比较数组的元素对象,还比较对象的内容。
10.将数组转成List集合: Arrays-asList方法
static <T> List<T> asList(T... a)
返回一个受指定数组支持的固定大小的列表。
原理:
/*
重点:List asList(数组) 将数组转成集合。
好处:可以使用集合的方法操作数组中的元素。
注意:
①数组的长度是固定的,所以对于集合的增删方法是不可以使用的。否则会发生java.lang.UnsupportedOperationException。
②如果数组中的元素是对象,那么转成集合时,直接将数组中的元素作为集合中的元素进行存储。
如果数组中的元素是基本数据类型数值,那么会将该数组作为集合中的元素进行存储。
int [] arr = {13,25,65};
List<int[]> list = Arrays.asList(arr);
原因:想知道数组是否包含某元素。
想知道元素在数组中的位置。
但是数组集合工具类中没有这些方法。
*/
public static void main(String[] args) {
String[] arr = {"abc","haha","xixi"};
boolean b = myContains(arr, "haha");
System.out.println(b);
List<String> list = Arrays.asList(arr);//转换成列表
boolean b1 = list.contains("haha");
System.out.println(b1);
// list.add("a"); //错误操作
}
public static boolean myContains(String[] arr, String key){
for (int i = 0; i < arr.length; i++) {
if(arr[i].equals(key))
return true;
}
return false;
}
11.将集合转成数组: Collection接口-toArray方法
Object[] toArray()
返回包含此 collection 中所有元素的数组。
T[] toArray(T[] a)
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。
原理:
/*
集合转换数组:
使用Collection接口中的toArray方法。
好处:可以对集合中的元素操作的方法进行限定。不允许对其进行增删。
*/
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
/*
Object[] toArray()
返回包含此 collection 中所有元素的数组。
*/
Object[] arr1 = list.toArray();
System.out.println(Arrays.toString(arr1));
/*
T[] toArray(T[] a)
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。
注意:
该方法需要传入一个指定类型的数组。
如果长度小于集合的size,那么该方法会创建一个同类型并和集合相同size的数组。
如果长度大于集合的size,那么该方法会使用指定的数组,存储集合中的元素,其他位置默认null。
建议,最后长度就指定为,集合的size.
*/
String[] arr = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(arr));
}
7.JDK1.5特性
①.JDK1.5给Collection接口找了个父接口Iterable:
实现这个接口允许对象成为 “foreach” 语句的目标。
foreach语句:底层还是实现的迭代。
格式:
for(类型 变量: Collection集合|数组)
{
}
举例:
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
/*
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
*/
foreach(String s:list){
System.out.println(s);
}
传统for和高级for的区别:
foreach局限性:
必须有遍历的目标(数组或Collection单列集合),一般只用于遍历,不会对元素进行过多的操作。是用来简化书写的。
传统for循环相对foreach的优势:
可以定义控制循环的条件和增量。
使用场景:
对数组的遍历如果仅仅是获取数组中的元素,可以使用foerach。
如果要对数组的角标进行操作建议使用传统for。
对于map集合,不能直接使用foreach遍历。但是可以将map转换成单列的set,就可以用了。
例子:
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("小米", 2000);
map.put("苹果", 5000);
map.put("三星", 4000);
//entrySet:
Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); //map变成set集合
for (Map.Entry<String, Integer> entry : entrySet) { //foreach遍历
System.out.println(entry.getKey() + ":" +entry.getValue());
}
//keySet:
for(String key : map.keySet()){
Integer value = map.get(key);
System.out.println(key + ": " + value);
}
//传统迭代:
Iterator<Map.Entry<String,Integer>> it= map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, Integer> me = it.next();
System.out.println(me.getKey() + ":" + me.getValue());
}
}
2.函数可变参数:int …
内部执行的动作:创建并将参数封装成数组。
其实就是一个数组,但是接收的是数组的元素。
自动将这些元素封装成数组。简化了调用者的书写。
注意事项:可变参数类型必须定义在参数列表的结尾处。
//求多个正数的和
public static void main(String[] args){
int sum = add(2,5,6,6);
System.out.println("sum= " + sum);
int sum1 = add(2,5,6);
System.out.println("sum1= " + sum1);
}
public static int add(int... arr){ //int... 和int[]等效,简化书写。调用传参的时候不需要建立数组,直接传值。
int sum = 0;
for(int i:arr)
sum+=i;
return sum;
}
3.静态导入:导入类的静态成员,方便程序简化书写。
因为静态成员不需要对象,直接使用,所以需要在程序加载前进行静态导入。
import static java.util.Collections.sort;
import static java.util.Collections.max;
或者:
import static java.util.Collections.*;
sort(list);
max(list);
import static java.lang.System.*;
out.println("123"); //out是静态的,println不是静态的。out对应一个对象。