集合
集合出现的意义
面向对象编程中,有的时候需要存储对象,集合就是一种方便的存储对象的形式。
集合的两大接口
Collection与map。
Collection
主要的子接口有:Set,List,Queue,SortedSet
Map
主要的子接口有:SortedMap
Set
Set是Collection的子接口,几乎与Collection一样,但是不能存储重复的元素,不能记住添加的顺序。Set判断两个对象的方式是equals而不是==。
常用的子类有HashSet,TreeSet。
HashSet
HashSet类是Set接口最常用的实现类,采用hash算法存储数据,具有良好的存储和查找功能。
特点:1,散列存储:不记录添加顺序;排列顺序时,顺序有可能发生变化。
2,线程不安全的,多个线程访问一个HashSet要使用同步代码。
3,HashSet集合元素值允许是null,但是最多只能有一个。
hash算法的功能:
保证通过一个对象快速找到另一个对象。
其算法价值体现在速度,可以保证查询快速执行。
执行方式:当从HashSet中访问元素时,HashSet先计算该元素的hashCode(也就是该对象的hashCode方法返回值),然后直接到该HashCode对应的位置取出该元素。
HashSet元素的添加:当添加元素的时候,会调用该对象的hashcode()方法来得到hashCode的值,判断已经存储在集合中的对象的hashCode值是否与添加的对
象的hashCode值一致:若不一致:直接添加进去;若一致,再进行equals方法比较,equals方法如果返回true,表明对象已经添加进去了,就不会再添加新的对象了,否则添加
进去。
如果我们重写了equals方法,也要重写hashCode方法,反之亦然。
HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。
如果需要某个类的对象保存到HashSet集合中,覆写该类的equals()和hashCode()方法,应该尽量保证两个对象通过equals比较返回true时,他们的hashCode返回也相等。
如下代码:package gyc.study.collection.set; import java.util.HashSet; import java.util.Set; /* * 此例子表明判断set必须有hashcode和equals两个方法 */ class C { @Override public int hashCode() { return 0; } } class D { @Override public boolean equals(Object o) { return true; } } class E { @Override public int hashCode() { return 0; } @Override public boolean equals(Object o) { return true; } } public class Demo1 { public static void main(String[] args) { C c = new C(); D d = new D(); E e = new E(); Set set = new HashSet<>(); set.add(c); set.add(d); set.add(e); System.out.println(set); } }
上面代码中,添加c,d成功但是e失败,是因为先调用e的hashCode方法,返回的值时0,在调用equals方法判断e与c是否相等,结果相等,所以e并没有添加进去到hashSet集合中。
TreeSet
使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序。参与排序的元素必须是同一类型的,不然会发生ClassCastException异常。
TreeSet是SortedSet接口唯一的实现。
当添加元素的时候,TreeSet会调用元素的compareTo(Object o)方法来比较元素之间的大小关系,然后将集合里的元素按升序排列.此时需要排序元素的类必须实现Compareble接口,并覆写其int compareTo(Object o)方法。判断两个对象相等的方法就是compareTo(Object o)返回值是0.
TreeSet还可以进行定制排序,只要创建时调用TreeSet(Comparator comparator)构造方法即可。Comparator是一个接口,包含了int compare(Object o1,Object o2)方法,用于比较两个对象的大小,比较结果和compareTo方法一致;。
代码如下
package gyc.study.collection.set; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; /* * 此例子表明指定比较方法 */ class Person2 { private Integer age; public Person2(int age) { this.age = age; } @Override public String toString() { return "Person [age=" + age + "]"; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } class C1 implements Comparator { @Override public int compare(Object o1, Object o2) { Person2 p1 = (Person2)o1; Person2 p2 = (Person2)o2; if(p1.getAge()>p2.hashCode()) { return 1; } else if(p1.getAge()<p2.hashCode()) { return -1; } return 0; } } public class Demo4 { public static void main(String[] args) { Set set = new TreeSet<>(new C1()); set.add(new Person2(100)); set.add(new Person2(12)); set.add(new Person2(13)); set.add(new Person2(-1)); System.out.println(set); } }
这样就可以进行定制排序了。
List
List是有序的集合,集合中每个元素都有对应的顺序序列。List集合可使用重复元素,可以通过索引来访问指定位置的集合元素(顺序索引从0开始),List集合默认
按元素的添加顺序设置元素的索引,比如第一个元素的索引就是0,好似数组。
List中还可以调用listIterator()来返回ListIterator对象,ListIterator是Iterator的子接口,可以实现双向输出。
List具体的实现类:1,Vector线程安全,但速度慢,已被ArrayList替代。
2,ArrayList线程不安全,查询速度快。
3,LinkedList链表结构,增删速度快。虽然ArrayList是线程不安全的,而Vector是线程安全的,但是即使这样,也不推荐使用Vector,因为Collections有方法可以得到线程安全的ArrayList对象。
Map
Map是一种映射关系,Map集合里存在两组值,一组是key,一组是value。Map里的key不允许重复。通过key总能找到唯一的value与之对应。
Map里的key集存储方式和对应的Set集合中的元素存储方式一致。
Map.Entry是Map接口的内部接口,专门用来保存key-value内容。
按照最正统的做法,所有的Map集合的内容都要依靠Iterator输出,以上虽然是完成了输出,但是完成的不标准,Map集合本身并不能直接为Iterator实例化,如果此时非要使用Iterator输出Map集合中内容的话,则要采用如下的步骤:
方法一:
1.通过entrySet方法变成Set对象
2.调用Set的Iterator方法,此时每个Iterator对象是Map.Entry对象
3.对Map.Entry分离出 key - value
方法二:
1.通过keySet得到Map集合多有key的Set集合
2.调用Set的Iterator方法,此时每个Iterator对象是key值
3.通过Map的getValue(key)得到value值
实现的类:Hashtable:线程安全,速度慢,不允许存放null键,null值,已被HashMap替代,无序存放。
HashMap:线程不安全,速度快,允许存放null键,null值。类中的key都属于无序存放的。即使HashMap线程不安全,在多线程中也推荐使用。因为Collections中有方法可以来返回线程同步的映射。
HashMap里的key存储和保存HashSet里面的元素一致,若自定义类作为HashMap或Hashtable的key:当两个key对象的equals方法返回true时,两个key对象的hashCode值也应一样。
TreeMap,由于HashMap子类中的key都属于无序存放的,如果现在希望有序(按key排序)则可以使用TreeMap类完成,但是需要注意的是,由于此类需要按照key进行排序,而且key本身也是对象,那么对象所在的类就必须实现Comparable接口。
当 使用 TreeMap 的时候 key不能为null,value可以是 null。下面代码展示如何用Map实现缓存功能。
package gyc.study.conllection.map; import java.util.HashMap; import java.util.Map; //该类用map实现了缓存的机制 class Axe { public void work() { System.out.println("砍人"); } } public class CacheDemo { public static void main(String[] args) { /* Axe axe = new Axe(); axe.work(); */ Axe axe = null; Map map = new HashMap<>(); if(!map.containsKey("myaxe")) { axe = new Axe(); map.put("myaxe", axe); } else { axe = (Axe)map.get("myaxe"); } } }
这样的话,每次需要斧子对象的时候就可以从Map中取得。
Properties类是Hashtable子类
Properties类的主要功能是用于操作属性,在各个语言(包括操作系统)都会存在着许多的配置文件。所有的属性文件中的属性都是按照“key=value”的形式保存的,而且保存的内容都是String(字符串)。
下面代码演示如何用Properties获取文件信息。
package gyc.study.Factory; import java.io.IOException; import java.io.InputStream; import java.util.InvalidPropertiesFormatException; import java.util.Properties; public class PhoneFactory { private static Properties p = null; private static final String path = "FactoryFile"; static { p = new Properties(); ClassLoader loader = PhoneFactory.class.getClassLoader(); InputStream io = loader.getResourceAsStream(path); try { p.load(io); } catch (InvalidPropertiesFormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void getPhone() { System.out.println(p); } public static void main(String[] args) { PhoneFactory.getPhone(); } }
集合总结
1,Set与Map联系密切,Map冲Key值的存储形势就是Set,而且两者有很多方法相似,实现类的命名也相似。
2,许多操作可以通过Collections,比如获取线程安全的类型,翻转List顺序。
3,多查API即可知道很多操作。
泛型
泛型的引入
泛型是在JAVA5中被引入,为了解决一个集合中类型一致,取出时需要强制转换的问题。泛型就是一种类型参数,可以在不确定类型的时候进行占位,在运行的时候
类型进行确定。
泛型类
在类声明时通过一个标识符表示类中某个字段的类型或者某个方法的返回值或参数的类型,这样在类声明或实例化的时候只要指定自己需要的类型即可。但是类型不可以是基本类型。
定义方式:class 类名<泛型类型1,泛型类型2……>
这样在类中就可使用泛型类型 变量名 这样的语法了。