一、set集合

set集合不根据索引去获取元素,想遍历结合也不能依靠普通的for循环。

遍历集合只能使用迭代器,新循环可以遍历,因为新循环的实现机制也是使用迭代器。

set常用实现类:

HashSet:使用哈希算法实现

TreeSet:使用二叉树实现

无法从Set集合中取出特定的元素。Set是不可重复的。

Set<String> set = new HashSet<String>();

set.add("one");

set.add("two");

set.add("three");

//遍历只能使用迭代器

Iterator<String> it = set.iterator();

while (it.hasNext()) {

   String string = it.next();

   System.out.println(string);

}


1、HashSet和hashCode方法的关系


HashSet是Set接口的实现类,通过哈希表实现;在将对象加入到HashSet集合中时,需要获取对象的hashCode值通过hash算法索引到对应的存储空间。


具体是:a.当一个对象要插入到HashSet中时,首选获取对象的hashCode值,进行hash运算确定存储空间;


b.当通过contains方法判断集合中是否包含某个对象时,需要首先根据该对象的hashCode值索引到特定的空间,然后在和空间中的对象调用equels方法进行比较,这种查找方式不同于线性表的逐个比较,有很高的效率。


HashSet在add时,会根据该元素的hashCode值进行计算,来决定元素的位置。返回的是一个整数。


set也是集合,也可以使用Comparable。


2、hashCode方法


对于重写了equals方法的对象,一般要妥善的重写继承自Object类的hashCode方法(Object提供的hashCode方法将返回该对象所在内存地址的整数形式)。


重写hashCode方法需要主义两点:其一,与equals方法的一致性,即equals比较返回true的两个对象的hashCode方法返回值应该相同;其二,hashCode返回的数值应该符合哈希算法的要求。试想,如果有很多对象的hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE提供的工具自动生成hashCode方法。


Object中提供的方法,默认是返回该对象的地址的整数形式。

注:对于重写了equals方法的类,应该也重写hashCode()。


重写hashCode的要求:

1)与equals保持一致,equals返回true的时候,两个对象的hashCode值也应该相同。

2)hashcode值应该是一个稳定的值,在对象内容不发生改变的情况下,hashCode()方法返回值不应该改变。所谓对象内容不发生变化,指的是参与equals比较逻辑的内容不发生变化。


每个集合的实现类都支持一个复制构造器。可以在创建该集合时将给定的集合中的元素存入当前集合。

注意:通过复制构造器我们仅仅是创建了一个新集合,对于元素来讲,并没有被复制!

public class Demo1_Set {
    public static void main(String[] args) {
        Set<Point> s = new HashSet<Point>();
        s.add(new Point(1,2));
        s.add(new Point(1,2));
        s.add(new Point(1,3));
        System.out.println(s);//[[x=1, y=3], [x=1, y=2]]
        
        //重写了hashCode方法,使其他们equals相等的hashCode也相等。
        
        //如果不重写hashCode方法,尽管他们的equals为true,
        
        //但是他们的hashCode返回不同,HashSet将不会给这两个对象进行equals比较的机会。
    }
}
class Point{
    private int x;
    private int y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    @Override
    public int hashCode() {
        //根据x y 数值计算出,如果两个对象的xy相等,则他们的hashCode也相等,显然符合equals方法原则。
        final int prime = 31;
        int result = 1;
        result = prime * result + x;
        result = prime * result + y;
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Point){
            return ((Point)obj).x == this.x && ((Point)obj).y == this.y;
        }
        return false;
    }
    @Override
    public String toString() {
        return "[x=" + x + ", y=" + y + "]";
    }
}


二、Map集合

Map定义的集合又称为查找表,用于存储所谓“Key-Value”映射对。作为key的对象在集合中不可以重复。

map不是Collection的子类,根据内部实现的不同,常见的是:

HashMap,使用Hash算法实现。

TreeMap,使用二叉树实现。

1、常用方法

存取元素的方法:

1)V put(K key, Value)将value以key作为一对存入map

key在整个map中不能重复,就是key类型的对象的equala返回不能是true

如果使用了重复的key这样就会替换了value。返回值就是使用相同key被替换的value,如是第一次存入数据,返回的是null。

2)V get(Object key)

根据给定的key获取对应的value,若该key在map中不存在则返回null.

3)boolean containsKey(Object key)

判断集合中是否包含指定key.

3)boolean containsValue(Object value)

判断集合中是否包含指定value.


例:

Map<String,Point1> hashmap = new HashMap<String,Point1>();
hashmap.put("p1", new Point1(1,2));
hashmap.put("p2", new Point1(4,3));
hashmap.put("p3", new Point1(1,2));
System.out.println(hashmap);
//如果使用了重复的key这样就会替换了value。
//返回值就是使用相同key被替换的value,如是第一次存入数据,返回的是null。
System.out.println(hashmap.put("p3", new Point1(12,10)));
//获取指定key的知
System.out.println(hashmap.get("p3"));
//判断集合中是否包含指定key.
System.out.println(hashmap.containsKey("p1"));
//判断集合中是否包含指定value.
System.out.println(hashmap.containsValue(new Point1(4,3)));

2、HashMap基本原理


HashMap存放元素时,会根据key的hashCode值进行哈希算法,用来计算这一组key-value应存放的位置,若key的hashCode值根据计算得到的位置已经有元素占用了,那么该值会放在那个元素的后边,因为HashMap中存放元素在位置相同的地方使用链表的形式存放每组数据的。

HashMap中存放数据的数组叫做散列数组。

容量:散列数组的大小。

初始容量:刚创建HashMap时,散列数组的大小默认16。

大小:hashmap中存储的元素数量。

加载因子:根据实验表明,加载因子保持在0.75一下,hashMap检索效率最高。

加载因子=大小/容量


鉴于hashmap的存储于原理,存入HashMap的key必须要妥善的重写hashCode方法。


HashMap遍历可以有三种方式:

1)只遍历key,使用keySet得到的是一个Set集合。

2)只遍历value,使用values得到的是一个Collection集合,如果以set返回会丢失元素。

3)遍历key-value对

例:

Map<String, Point2> map = new HashMap<String, Point2>();
map.put("p1", new Point2(2,2));
map.put("p2", new Point2(3,2));
map.put("p3", new Point2(5,6));
/**
* keySet()方法,将map中所有的key以Set集合的形式返回。
*/
Set<String> keySet = map.keySet();
for (String string : keySet) {
    System.out.print(string+" ");//p3 p2 p1
}
System.out.println(keySet);//[p3, p2, p1]
/**
* values()方法,获取map中所有的value值
* 返回不是Set集合,是list集合。
*/
Collection<Point2> c = map.values();
System.out.println(c);//[[x=5, y=6], [x=3, y=2], [x=2, y=2]]
/**
* 遍历key-value对
* entrySet():将map中的元素以key-value对存入set集合并返回
* key-value对,map用其一个内部类Entry的实例去保存。
* Entry支持泛型。它的泛型定义应该和map的泛型保持一致。
*/
Set<Entry<String, Point2>> entries = map.entrySet();
for (Entry<String, Point2> entry : entries) {
    /**
    * 通过Entry的方法获取key和value
    */
    System.out.println(entry.getKey()+":"+entry.getValue());
    //p3:[x=5, y=6]
    //p2:[x=3, y=2]
    //p1:[x=2, y=2]
}
//或:
Iterator<Entry<String, Point2>> it = entries.iterator();
while (it.hasNext()) {
    Entry<String, Point2> entry = it.next();
    System.out.println(entry.getKey()+":"+entry.getValue());
}