1. 程式人生 > 其它 >【JavaSE】---泛型與容器類

【JavaSE】---泛型與容器類

【JavaSE】---泛型與容器類

一、泛型

1、泛型的概念

​ 我們知道java.lang.Object類是最上層的類,它是所有類的父類。所以為了讓程式通用,編寫程式碼時候通常使得傳入的值與返回的值都用Object型別為主,當需要使用相應的例項時候,必須正確地將該例項轉換為正確的例項。否則程式在執行的時候會報出型別強制轉換異常:ClassCastException。

​ 通過泛型技術,這樣的問題得以解決。使用泛型的主要優點是能夠在編譯時而不是在執行時檢測出錯誤。泛型實際上是在定義類、介面或者方法時通過為其增加“引數型別”來實現的。

​ 即泛型所操作的資料型別被指定為一個引數,這個引數被稱為型別引數(type parameters),

所以說,泛型的實質是將資料的型別引數化,按照慣例,用T或者E這樣的單個大寫字母來表示型別引數。

2、泛型的定義

  • 泛型類的定義:
[修飾符] class 類名 <T>
  • 泛型介面的定義:
[public] interface 介面名 <T>
  • 泛型方法的定義:
[public] [static] <T> 返回值型別 方法名(T引數)

泛型的實際引數型別必須是 類 型別,利用泛型類建立的物件稱為泛型物件,這個過程也稱之為泛型例項化。

泛型的概念實際上是基於:型別也可以像變數一樣實現引數化。

3、泛型類及應用

示例:

public class FanClass <T>{

    private T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }

    public static void main(String[] args) {
        FanClass<String> name = new FanClass<String>();   //建立FanClass<String>型的物件
        FanClass<Integer> age = new FanClass<>();       //建立FanClass<Integer>型的物件

        name.setObj("darkerg");
        age.setObj(25);

        System.out.println("姓名:"+name.getObj());
        System.out.println("年齡:"+age.getObj());
    }
}

4、泛型方法

​ 要定義泛型方法,只需將泛型的型別引數<T>置於方法返回值型別前即可。在Java中,任何方法(包括靜態方法和構造器)都可以宣告為泛型方法。

程式示例

public class FanMethod {

    public static void main(String[] args) {
        Integer[] num = {1,2,3,4,5};//定義陣列
        String[] str = {"紅","橙","黃","綠"};

        display(num);
        display(str);
    }

    public static <E> void display(E[] list){//定義泛型方法,E為型別引數。

        for (int i = 0; i < list.length; i++) {
            System.out.print(list[i]+" ");
        }
        System.out.println();
    }

}

5、限制泛型的可用型別

語法:

class ClassName <T extends anyClass>

<T> = <T extends Object>

該語句表示T是ClassName類的型別引數,且T有一個限制,即T必須是anyClass類或者是繼承了anyClass類的子類,或者是實現了anyClass介面的類。這意味著,當用該類建立物件時,型別實際引數必須是anyClass類或其子類是實現了anyClass介面的類,且無論是anyClass是類還是介面,在進行泛型限制時都必須使用extends關鍵字。

注意:

在Java中,子類被認為和父類是相容的型別。但是在使用泛型的時候,要注意它們之間的關係。

例如:

​ 雖然Integer是Number的子類,但是GeneralType<Integer>與GeneraType<Number>沒有父子關係。

​ 即GeneralType<Integer>不是GeneraType<Number>的子類。

​ 這種限定稱為:泛型不是協變的。

6、泛型的型別萬用字元和泛型陣列的應用

除了上面提到的有限制的泛型類之外,還引入了萬用字元“?”的概念。

  • 用於建立可以 重新賦值 但是不可以 修改 其內容的泛型物件。
  • 方法的引數中,限制傳入不想要的型別實參。

①使用同一個泛型物件名引用不同的泛型物件

​ 只知道萬用字元“?”表示是某個類又或者是 繼承該類的子類 又或者是 實現某個介面的類,但是具體是什麼型別不知道。

泛型類名<? extends T> o = null;		//宣告泛型類物件o

​ 其中,“?extends T" 表示是T 或者T的未知子型別 或者是實現介面T的類。

案例:

我們知道,java.util.LinkedList與java.util.ArrayList均是實現了介面java.util.List的類。

FanClass <? extends List> x = null;
x = new FanClass<LinkedList>();		//LinkedList類實現了List介面
//對x重新賦值,只要型別實參實現了List介面即可。
x = new FanClass<ArrayList>();     //ArrayList實現了List介面

//由於HashMap沒有實現List介面,下面語句是錯誤的。
FanClass<? extends List>x = new FanClass<HashMap>();

②?用在方法的引數中以防止傳入不允接收的型別實參

demo

public class FanClass_02 {
    public static void main(String[] args) {
        GeneralType<String> n = new GeneralType<>();

        n.setObj("darkerg");
        GeneralType.showObj(n);

        GeneralType<Double> num = new GeneralType<>();
        num.setObj(25.0);
        System.out.println("數值型值:"+num.getObj());//不可以呼叫方法showObj(num)
    }
}

class GeneralType<T>{
    T obj;

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }

    public static void showObj(GeneralType<? extends String>o){
        System.out.println("給出的值是:"+o.getObj());
    }
}

7、繼承泛型類與實現泛型介面

被定義為泛型的類或者或者介面可以被繼承與實現:

public class ExtendClass<T1>{
    
}
class SubClass<T1,T2,T3> extends ExtendClass<T1>

如果在SubClass類繼承ExtendClass類時保留父類的型別引數,需要在繼承的時候指明,如果沒有指明,直接使用extends ExtendClass語句進行繼承宣告,則SubClass類中的T1、T2和T3都會自動變為Object所以在一般情況下都將父類的型別引數保留。

在定義泛型介面的時,泛型介面也可被實現。

interface Face<T1>{
    
}
class SubClass<T1,T2> implements Face<T1>{
    
}

二、容器類

​ 容器類是Java以類庫的形式供使用者開發程式時可直接使用的各種資料結構。在面向物件的思想裡,一種資料結構被認為是一個容器。陣列是一種簡單的資料結構,除陣列外Java還以類庫的形式提供了許多其他資料結構。這些資料結構通常稱為容器類或者集合類。

1、Java容器框架

​ 容器框架中有兩個名稱分別為Collection和Set的介面,為了防止名稱的衝突。將Collection稱為容器,將Set稱為集合。

  • Collection介面,定義了所有容器的基本操作,比如新增、刪除、遍歷等等。
    • Set、List等則提供了更加特殊的功能
    • Set用來儲存一組不重複的元素集合
    • List的物件用於儲存一個由元素構成的線性表
  • Map介面,它保持了“鍵”到“值”的對映,可以通過鍵來實現對值得快速訪問。

2、容器介面Collection

​ 容器介面通常不能直接使用,但是該介面提供了新增、刪除、管理資料元素的方法。Set和List都繼承了Collection介面,因此這些方法對集合Set與列表List是通用的。

Collection<E>介面的常用方法

常用方法 功能說明
int size() 返回容器中元素的個數
boolean isEmpty() 判斷容器是否為空
boolean contains(Object obj) 判斷容器是否包含元素obj
boolean add(E element) 向容器中新增元素element,成功返回true,否則false
int hashCode() 返回容器的雜湊值
Object[] toArray() 將容器轉換為陣列,返回的陣列包含容器的所有元素
boolean remove(Object obj) 從容器中刪除元素obj,成功返回true,否則false
void clear() 刪除容器中的所有元素
Iterator<E> iterator() 返回容器的迭代器
boolean equals(Object o ) 比較此collection與指定物件o是否相等
void shuffle(<List<?> list) 以隨機方式重排list中的元素,即洗牌
boolean containsAll(Collection<?> c) 判斷當前容器是否包含容器c中的所有元素
boolean addAll(Coolection<?extends E>c) 將容器c中的所有元素新增到當前容器中,集合並運算
boolean removeAll(Collection<?> c) 在當前容器中刪除包含在c中的所有元素,集合差運算
boolean retainAll(Collection<?> c) 僅保留當前容器中也被容器c包含的元素,集合交運算

3、列表介面List

​ List是Coleection的子介面,它是一種包含有序元素的現行七寶,其中的元素必須按照順序存放,並且可以重複,也可以是空值null。元素之間的順序關係可以由新增到列表的先後來覺得。也可以由元素值得大小來決定。List介面適用下標來訪問元素。

​ 下標範圍為0~size()-1

List<E>介面的常用方法

常用方法 功能說明
E get(int index) 返回列表中指定位置的元素
E set(inr index,E elemenyt) 用元素element取代index位置的元素,返回被取代的元素
int indexOf(Object o ) 返回元素o首次出現的序號,若o不存在返回-1
int lastIndexOf(Object o ) 返回元素o最後出現的序號
void add(int index,E element) 在index位置插入元素element
boolean add(E element) 在列表的最後日安家元素element
E remove(int index) 在列表中刪除index位置的元素
boolean addAll(Collection<? extends E> c) 在列表的最後新增容器c中的所有元素
boolean addAll(int index,Collection<? extends E> c) 在index位置按照容器c中元素的原有次序插入c中所有元素
ListIterator<E> listIterator() 返回列表中元素的列表迭代器
ListIterator<E>listIterator(int index) 返回從index位置開始的列表迭代器

①連結串列實現類---LinkedList

​ LinkedList連結串列類採用連結串列結構儲存物件,使用迴圈雙鏈表實現List。這種結構向連結串列中任意位置插入、刪除元素時不需要移動其他元素,連結串列的大小可以動態增大或者減小的,但是不具有隨機存取的特性。

LinkedList<E>類的構造方法

構造方法 功能說明
public LinkedList() 建立空連結串列
public LinkedList(Collection <? extends E> c) 建立包含容器c中所有元素的連結串列

LinkedList<E>類的常用方法

常用方法 功能說明
public void addFirst(E e) 將元素e插入到列表的開頭
public void addLast(E e) 將元素e新增到列表的末尾
public E getFirst()
public E getLast()
public E removeFirst() 刪除並返回列表中的第一個元素
public E removeLast()

②陣列列表類實現類---ArrayList

​ ArrayList陣列列表類使用一維陣列實現List,該類實現的時可變陣列,允許所有元素,包括null。具有隨機存取的特性,插入、刪除時候需要移動其他元素,當元素很多的時候插入。刪除操作比較慢。

ArrayList<E>類的構造方法

構造方法 功能說明
public ArrayList() 建立初始容量為10的空陣列列表
public ArrayList(int initialCapacity) 建立初始容量為initialCapacity的空陣列列表
public ArrayList(Collection<? extends E>c) 建立包含容器c所有元素的陣列列表,元素次序與c相同

ArrayList<E>類的常用方法

常用方法 功能說明
public void trimToSize() 將ArrayList物件的容量縮小到該列表的當前大小
public void forEach(Consumer<? super E> action) 將action物件執行遍歷操作

③使用

  • 若要通過下標隨機訪問元素,但除了在末尾處之外,不再其他位置插入或者刪除元素,應該選擇ArrayList。
  • 如果需要線上性表的任意位置上進行插入或者刪除操作,應該選擇LinkedList類

使用線性表的時候通常宣告為List<E>型別,然後通過不同的實現類來例項化列表。

List<String> list1 = new LinkedList<String>();
List<String> list2 = new ArrayList<String>();

④利用LinkedList<E>類構造一個先進後出的堆疊

StringStack

package 堆疊;

import java.util.LinkedList;

//利用LinkedList<E>類構造一個先進後出的堆疊
public class StringStack {

    private LinkedList<String> Id = new LinkedList<>();//建立LinkedList物件Id

    public void push(String name){                  //入棧操作
        Id.addFirst(name);                      //將name新增到連結串列的頭
    }

    public String pop(){                        //出戰操作
        return Id.removeFirst();            //獲取並移除堆疊中的第一個元素
    }

    public boolean isEmpty(){
        return Id.isEmpty();
    }

}

Test

package 堆疊;

import java.util.Scanner;

public class Test {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        StringStack stack = new StringStack();

        System.out.println("請輸入資料(quit)結束");

        while (true){
            String input = sc.next();
            if (input.equals("quit")){
                break;
            }
            stack.push(input);
        }

        System.out.println("先進後出的順序:");
        while (!stack.isEmpty()){
            System.out.print(stack.pop()+" ");//出棧
        }
    }

}

4、迭代器

​ 迭代功能由可迭代介面Iterable和迭代器介面Iterator、ListIterator實現的,迭代器是一種允許對容器中元素進行遍歷並且有選擇的刪除元素的物件。由於Collection介面宣告繼承了Iterable介面,因此每個實現了Collection介面的容器物件都可以呼叫iterator()方法返回一個迭代器。

Iterator<E>介面的常用方法:

常用方法 功能說明
public abstract boolean hasNext() 判斷是否還有後續元素,若有則返回true。
public abstract E next() 返回後續元素
public abstract void remove() 刪除迭代器當前指向的(即最後被迭代的)元素,即刪除由最近一次next()或者previous()方法呼叫返回的元素

使用迭代器遍歷容器的程式碼段:

Iterator it = c.iterator();		//c是重寫了iterator()方法的容器所實現的類  的物件
while(it.hasNext()){
    Object o = it.next();		//取得下一個未被迭代的元素
}

​ 對於容器中元素的遍歷次序,介面Iterator支援對List物件從前向後的遍歷,但其子介面ListIterator支援對List物件的雙向遍歷。

ListIterator<E>介面的常用方法

常用方法 功能說明
public abstract boolean hasPrevious() 判斷是否有前驅元素
public abstract E previous() 返回前驅元素
public abstract void add(E e) 將指定的元素插入到列表中。若next()方法的返回值為非空,鈣元素被插入到next()方法返回的元素之前;若previous()方法的返回值為非空,該元素被插入到previous()方法返回的元素之後;若線性表沒有元素,則直接將該元素加入其中
public abstract void set(E e) 用元素e替換列表的當前元素
public abstract int nextIndex() 返回基於next()呼叫的元素序號
public abstract int previousIndex() 返回基於previous呼叫的元素序號

修改遍歷迭代器應用

package 迭代器;

import java.util.ArrayList;
import java.util.ListIterator;

public class IteraTest {

    public static void main(String[] args) {
        ArrayList<Integer> a1 = new ArrayList<>();
        for (int i = 1; i < 5; i++) {
            a1.add(i);
        }
        System.out.println("陣列列表的原始資料:"+a1);

        ListIterator<Integer> listIter = a1.listIterator();
        listIter.add(0);        //在需要為0的元素前新增一個元素0
        System.out.println("新增後陣列列表"+a1);

        if (listIter.hasNext()){//如果有後續元素
            int i = listIter.nextIndex();       //執行該語句時i的值是1
            listIter.next();                    //返回序號為1的元素
            listIter.set(9);                    //修改陣列列表a1中序號為1的元素
            System.out.println("修改後陣列列表"+a1);
        }

        listIter = a1.listIterator(a1.size());  //重新建立從a1最後位置開始的迭代器listIter
        System.out.print("反向遍歷陣列列表:");
        while (listIter.hasPrevious()){
            System.out.print(listIter.previous()+" ");//反向遍歷陣列列表
        }
    }

}

5、集合介面Set

​ Set是一個不含重複元素的集合介面,它繼承自Collection介面,並沒有宣告其他方法,它的方法都是從Collection介面繼承來的。

Set集合中的物件不按照特定的方式排序,只是簡單地吧物件加入到集合中即可,但是加入的物件一定不能重複。集合中元素的順序與元素加入集合的順序無關。

①雜湊集合類---HashSet

​ 雜湊集合對 所包含元素的訪問並不像是線性表一樣使用下標,而是根據雜湊值來存取集合中的元素。雜湊集合是在元素的儲存位置和元素的值k之間建立一個特定的對應關係f,使得每個元素與一個唯一的儲存位置相對於。因而在查詢的時候,只需要根據元素的值k,計算f(k)的值即可,如果次元素在集合中,那麼必定存在儲存位置f(k)上,因此不需要與集合中的其他元素進行比較便可以直接取得所查的元素。稱這個對應關係f為雜湊函式,按照這種關係建立的表稱之為雜湊表,也稱為散列表。

​ HashSet集合不保證迭代順序,但允許元素值為null。在比較啷個加入雜湊集合HashSet中的元素是否相同時,會先比較雜湊值方法hashCode()的返回值是否想吐,如果相同則再使用equals()方法比較其儲存位置(即記憶體地址),若兩者相同則視為相同的元素。之所以在比較了雜湊值之後,還需要通過equals方法進行比較,是因為可能出現雜湊碰撞。所以,對於雜湊集合來說,如果重寫了元素對於類的equals()方法或者hashCode()方法中的某一個,則必須重寫另一個,以保證其判斷的一致性。

HashSet<E>集合類的構造方法

構造方法 功能說明
public HashSet() 建立預設初始容量是16,預設上座率為0.75的空雜湊集合。
public HashSet(int initialCapacity) 建立初始容量是initialCapacity,預設上座率為0.75的空雜湊集合。
public HashSet(int initialCapacity,float loadFactor) 建立初始容量是initialCapacity,預設上座率為loadFactor的空雜湊集合。
public HashSet(Collection<? extends E>c) 建立包含容器c中所有元素,預設上座率為0.75的雜湊集合。
  • 說明:上座率也稱為裝填銀子,上座率的值為0.0~1.0表示集合的飽和度。當集合中的元素個數超過了容量與上座率的乘積,容量就會自動翻倍。

HashSet<E>集合類的常用方法

常用方法 功能說明
public boolean add(E e) 如果集合中常委包含指定元素,則新增返回true。
public void clear() 刪除集合中的所有元素,集合為 空
public boolean contains(Object o) 如果集合中包含元素o,則返回true
public int size() 返回集合中所包含元素的個數,即返回集合的容量

測試:

package Set;

import java.util.*;

public class SetTest {
    public static void main(String[] args) {

        HashSet<String> hs = new HashSet<String>();
        String[] demo = {"I","come","I","see","I","go"};

        for (String s : demo) {
            if (!hs.add(s))         //向雜湊集合hs中新增元素,但是重複的元素不新增。
                System.out.println("元素"+s+"重複!");
        }

        System.out.println("集合的容量為:"+hs.size()+"各元素為:");
        Iterator<String> it = hs.iterator();
        while (it.hasNext())
            System.out.print(it.next()+" ");

    }
}
  • 輸出雜湊集合元素時候並不一定是按照元素的儲存順序輸出,因為雜湊集合中的元素是沒有特定的順序的,如果一定要讓元素有序輸出,則需要使用LinkedHashSet類。

②樹集合類---TreeSet

​ 樹集合類TreeSet不僅實現了Set介面,還實現了java.util.SortedSet介面。TreeSet的工作原理與HashSet相似,但是TreeSet增加了一個額外的步驟,用來保證集合中的元素總是處於有序的狀態。因此,當排序很重要的時候,就選擇TreeSet。TreeSet<E>類的大多數方法繼承自其父類或者祖先類。

TreeSet<E>類的構造方法

構造方法 功能說明
public TreeSet() 建立新的空樹集合,其元素按照自然順序進行排序
public TreeSet(Collection<? extends E> c) 建立包含容器c元素的新TreeSet

TreeSet<E>類新增的方法

新增的方法 功能說明
public E first() 返回集合中的第一個(最低)元素
public E last() 返回集合中的最後一個(最高)元素
public SoredSet<E> headSet(E toElement) 返回一個新集合,元素是toElement之前的所有元素
public SoredSet<E> tailSet(E fromElement) 返回一個新集合,包含fromElement及fromElement之後的所有元素
public SoredSet<E> subSet(E fromElement,E toElement) 返回一個新集合,包含從fromElement到toElement之間的所有元素
public E lower(E e) 返回嚴格小於給定元素e的最大元素,如不存在,返回Null
public E higher(E e) 返回嚴格大於給定元素e的最小元素,如不存在,返回Null
public E floor(E e) 返回小於等於給定元素e的最大元素,如不存在,返回Null
public E ceiling(E e) 返回大於等於給定元素e的最小元素,如不存在,返回Null

測試:

package Set;

import java.util.HashSet;
import java.util.TreeSet;

public class TreeSetTest {

    public static void main(String[] args) {
        HashSet<String> hs = new HashSet<>();
        String[] demo = {"唐 僧","孫悟空","豬八戒","沙和尚","白龍馬"};
        for (String s : demo) {
            hs.add(s);
        }

        TreeSet<String> ts = new TreeSet<>(hs);
        System.out.println("樹集合:"+ts);
        System.out.println("第一個元素"+ts.first());
        System.out.println("最後一個一個元素"+ts.last());
        System.out.println(ts.headSet("孫悟空"));
        System.out.println(ts.tailSet("孫悟空"));
        System.out.println(ts.ceiling("沙"));
    }

}

6、對映介面Map

​ Map是另一種儲存資料結構的物件,它提供了鍵值對的對映。值是指要存入Map中的元素(物件),在將元素存入Map物件時,需要同時給定一個鍵,這個鍵決定了元素在Map中的儲存位置。一個鍵和它對應的值構成一個條目,真正在Map中儲存的是這個條目。

Map<K,V>介面的常用方法

常用方法 功能說明
V put(K key, V value) 以key為鍵,向集合中新增值為value的元素,key必須唯一。
void putAll(Map <? extends K, ? extends V> m) 將對映m中的所有對映關係複製到呼叫此方法的對映中
boolean containsKey(Object key) 判斷是否包含指定的鍵key
boolean containsValue(Object value) 判斷是否包含指定的值value
V get(Object key) 返回鍵key所對映的值,若key不存在則返回null
Set <K> keySet() 返回該對映中所有鍵物件形成Set集合
Collection<V> values() 返回該對映中所有值物件形成Collection集合
V remove(Object key) 將鍵為key的條目,從Map物件中刪除

​ 對映介面Map常用的實現類有雜湊對映HashMap和樹對映TreeMap,HashMap對映是基於雜湊表的Map介面的實現類,所以HashMap通過雜湊值對其內部的對映關係進行快速查詢,因此對於新增和刪除對映關係效率較高,並且允許使用null值和Null鍵,但是必須保證鍵的唯一性;而樹對映TreeMap中的對映關係存在一定的順序,如果希望Map對映中的元素也存在一定的順序,應該使用TreeMap類實現的Map對映,由於TreeMap類實現的Map對映中的對映關係是根據鍵物件按照一定的順序排列的,因此不允許鍵物件是null。

①HashMap<K,V>對映常用的構造方法

構造方法 功能說明
public HashMap() 構造一個具有預設初始容量(16)和預設上座率0.75的HashMap物件
public HashMap(int initialCapacity) 建立初始容量為initialCapacity和預設上座率0.75的空HashMap物件
public HashMap(Map<? extends K, ? extends V> m) 建立一個對映關係與指定Map相同的新HashMap物件

②TreeMap<K,V>對映的構造方法

構造方法 功能說明
public TreeMap() 使用鍵的自然順序建立一個新的空樹對映
public TreeMap(Map<? extends K,extends V> m) 建立一個與給定對映具有相同對映關係的新樹對映
public K firstKey() 返回對映中第一個鍵
public K lastKey() 返回對映中的最後一個鍵
public SortedMap<K,V> headMap(K toKey) 返回鍵值小於toKey的那部分對映
public SortedMap<K,V>tailMap(K fromKey) 返回鍵值大於或等於fromKey的那部分對映
public K lowerKey(K key) 返回嚴格小於給定鍵key的最大鍵,如不存在,返回null
public K floorKey(K key) 返回小於或者等於key的最大鍵
public K higherKey(K key) 返回嚴格大於給定鍵key的最小鍵
public K ceilingKey(K key) 返回大於或者等於Key的最小建

③測試

package Map;

import java.util.*;

public class TestMap {

    public static void main(String[] args) {
        Map<String,String> hm = new HashMap<String,String>();

        hm.put("006","唐 僧");
        hm.put("008","孫悟空");
        hm.put("009","豬八戒");
        hm.put("007","沙和尚");
        hm.put("010","白龍馬");

        System.out.println("雜湊對映中的內容如下:\n"+hm);
        String str =(String) hm.remove("010");
        Set<String> keys = hm.keySet();
        Iterator<String> it = keys.iterator();
        System.out.println("HashMap類實現的Map對映,無序:");
        while (it.hasNext()){
            String xh =(String) it.next();
            String name =(String) hm.get(xh);
            System.out.println(xh+" "+name);
        }

        TreeMap<String, String> tm = new TreeMap<>();
        tm.putAll(hm);
        Iterator<String> iter = tm.keySet().iterator();//獲取迭代器
        System.out.println("TreeMap類實現的Map對映,鍵值升序:");
        while (iter.hasNext()){
            String xh = iter.next();
            String name = hm.get(xh);
            System.out.println(xh + " "+name);
        }

    }
}