วิธีอัปเดตค่ากำหนดรหัสใน hashmap ได้อย่างไร


624

สมมติว่าเรามีHashMap<String, Integer>ภาษาจาวา

ฉันจะอัพเดต (เพิ่มขึ้น) จำนวนเต็มของสตริง - คีย์สำหรับการมีอยู่ของสตริงที่ฉันค้นหาแต่ละครั้งได้อย่างไร

หนึ่งสามารถลบและป้อนคู่อีกครั้ง แต่ค่าใช้จ่ายจะเป็นปัญหา
อีกวิธีหนึ่งคือเพียงแค่ใส่คู่ใหม่และแทนที่เดิม

ในกรณีหลังนี้จะเกิดอะไรขึ้นหากมีการชนกันของรหัสแฮชโค้ดกับรหัสใหม่ที่ฉันพยายามแทรก พฤติกรรมที่ถูกต้องสำหรับ hashtable คือการกำหนดสถานที่อื่นให้หรือทำรายการจากมันในที่ฝากข้อมูลปัจจุบัน

คำตอบ:


972
map.put(key, map.get(key) + 1);

ควรจะดี มันจะปรับปรุงค่าสำหรับการทำแผนที่ที่มีอยู่ โปรดทราบว่านี่ใช้มวยอัตโนมัติ ด้วยความช่วยเหลือของmap.get(key)เราได้รับค่าของคีย์ที่เกี่ยวข้องจากนั้นคุณสามารถอัปเดตตามความต้องการของคุณ ที่นี่ฉันกำลังปรับปรุงเพื่อเพิ่มมูลค่าโดย 1


21
ในความเป็นจริงนั้นเป็นโซลูชันระดับองค์กรที่แข็งแกร่งและปรับขนาดได้
Lavir the Whiolet

12
@ Lavir มันไม่ใช่ทางออกที่ดี แต่ไม่เห็นว่ามันแข็งแกร่งและปรับขนาดได้มากที่สุด Atomicinteger แทนสามารถปรับขนาดได้มากกว่า
John Vint

13
นี่ถือว่ากุญแจอยู่ใช่ไหม? ฉันได้รับ nullPointer Exception เมื่อไม่เป็นเช่นนั้น
เอียน

84
ด้วย Java 8 คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดยgetOrDefaultง่ายตัวอย่างเช่น:map.put(key, count.getOrDefault(key, 0) + 1);
Martin

2
@Martin .. map.put (คีย์, map.getOrDefault (คีย์, 0) + 1)
Sathesh

112

Java 8 วิธี:

คุณสามารถใช้computeIfPresentวิธีการและจัดหาฟังก์ชั่นการทำแผนที่ซึ่งจะถูกเรียกให้คำนวณค่าใหม่ตามที่มีอยู่

ตัวอย่างเช่น,

Map<String, Integer> words = new HashMap<>();
words.put("hello", 3);
words.put("world", 4);
words.computeIfPresent("hello", (k, v) -> v + 1);
System.out.println(words.get("hello"));

อีกวิธีหนึ่งคือคุณสามารถใช้mergeวิธีการที่ 1 เป็นค่าเริ่มต้นและฟังก์ชั่นการเพิ่มมูลค่าที่มีอยู่โดย 1:

words.merge("hello", 1, Integer::sum);

นอกจากนี้ยังมีเป็นพวงของวิธีการที่มีประโยชน์อื่น ๆ เช่นputIfAbsent, getOrDefault, forEachฯลฯ


3
ฉันเพิ่งทดสอบวิธีแก้ปัญหาของคุณ อันที่สองคืออันที่มีการอ้างอิงวิธีการทำงาน อันแรกแลมบ์ดาหนึ่งแสดงออกไม่ทำงานอย่างต่อเนื่องเมื่อมูลค่าของแผนที่ของคุณคือnull(พูดwords.put("hello", null);) ผลที่ได้ยังnullไม่1เป็นไปตามที่ฉันคาดหวัง
Tao Zhang

4
จาก Javadoc: "หากค่าสำหรับคีย์ที่ระบุมีอยู่และไม่เป็นโมฆะพยายามคำนวณการแม็พใหม่" คุณสามารถใช้compute()แทนมันจะจัดการกับnullค่าเช่นกัน
damluar

ฉันต้องการที่จะเพิ่มมูลค่าของฉันโดย 1. เป็นวิธีการแก้ของฉันด้วย.merge Integer::sum
S_K

48
hashmap.put(key, hashmap.get(key) + 1);

วิธีputนี้จะแทนที่ค่าของคีย์ที่มีอยู่และจะสร้างถ้าไม่มี


55
nullPointer Exceptionไม่มีก็ไม่ได้สร้างมันให้
smttsp

13
รหัสคือคำตอบที่ถูกต้องสำหรับคำถามที่กำหนด แต่ถูกโพสต์หนึ่งปีหลังจากที่โพสต์รหัสเดียวกันที่แน่นอนในคำตอบที่ยอมรับ สิ่งที่แตกต่างคำตอบนี้คือการระบุใส่สามารถสร้างรายการใหม่ซึ่งสามารถทำได้ แต่ไม่ได้อยู่ในตัวอย่างนี้ หากคุณกำลังใช้ hashmap.get (กุญแจ) สำหรับคีย์ / ค่าที่ไม่มีอยู่คุณจะได้รับ null และเมื่อคุณพยายามเพิ่มขึ้นตามที่ @smttsp บอกว่าจะเป็น NPE -1
Zach

8
คำตอบนี้ผิด NullPointerException สำหรับคีย์ที่ไม่มีอยู่
Eleanore

@smttp NullpointterException เฉพาะเมื่อคุณไม่ได้กำหนดค่าเริ่มต้น (อย่างที่คุณรู้ว่าคุณไม่สามารถเพิ่มค่าว่างได้)
Mehdi

การทำซ้ำและคำอธิบายที่ไม่ถูกต้อง ... และจะสร้างขึ้นหากไม่มีอยู่คุณไม่สามารถทำได้null + 1เนื่องจากจะพยายามยกเลิกการทำเครื่องหมายnullในจำนวนเต็มเพื่อทำการเพิ่ม
AxelH

43

Java 8ทางง่าย:

map.put(key, map.getOrDefault(key, 0) + 1);

สิ่งนี้ใช้วิธีการ HashMap ที่ดึงค่าสำหรับคีย์ แต่ถ้าคีย์ไม่สามารถเรียกคืนได้จะส่งคืนค่าเริ่มต้นที่ระบุ (ในกรณีนี้คือ '0')

สิ่งนี้ได้รับการสนับสนุนภายในคอร์ Java: HashMap <K, V> getOrDefault (คีย์วัตถุ, V defaultValue)


3
นี่เป็นสิ่งที่ดีกว่าในกรณีที่คุณใช้จาวา 1.8
Hemant Nagpal

30

แทนที่Integerด้วยAtomicIntegerและเรียกหนึ่งในincrementAndGet/ getAndIncrementวิธีการนั้น

อีกทางเลือกหนึ่งคือการห่อintในMutableIntegerชั้นเรียนของคุณเองซึ่งมีincrement()วิธีการคุณมีความกังวลเธรดเฉพาะเพื่อแก้ปัญหา


37
AtomicInteger เป็นจำนวนเต็มไม่แน่นอน แต่สร้างขึ้นภายใน ฉันสงสัยอย่างจริงจังในการเขียน MutableInteger ของคุณเองเป็นความคิดที่ดีกว่า
Peter Lawrey

กำหนดเองMutableIntegerดีกว่าAtomicIntegerใช้volatileซึ่งมีค่าใช้จ่าย ฉันต้องการใช้แทนint[1] MutableInteger
Oliv

@Oliv: ไม่พร้อมกัน
BalusC

@BalusC แต่ยังคงมีความผันผวนการเขียนมีราคาแพงกว่า มันทำให้แคชใช้ไม่ได้ หากไม่มีความแตกต่างตัวแปรทั้งหมดจะเปลี่ยนแปลงได้
Oliv

@Oliv: คำถามระบุถึงการชนกันของแฮชโค้ดอย่างชัดเจนดังนั้นการทำงานพร้อมกันจึงเป็นสิ่งสำคัญสำหรับ OP
BalusC

19

โซลูชันเดียว:

map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1);

4
แต่นั่นไม่ได้เพิ่มอะไรใหม่ให้กับคำตอบที่มีอยู่ใช่ไหม?
Robert

1
ใช่. คำตอบที่ทำเครื่องหมายถูกต้องจะโยน NullPointerException หากไม่มีคีย์ วิธีนี้จะทำงานได้ดี
Hemant Nagpal

18

@ ทางออกของ Matthew เป็นวิธีที่ง่ายที่สุดและจะทำงานได้ดีพอในกรณีส่วนใหญ่

หากคุณต้องการประสิทธิภาพสูง AtomicInteger เป็นทางออกที่ดีกว่า ala @BalusC

อย่างไรก็ตามการแก้ปัญหาที่รวดเร็วกว่า (ความปลอดภัยของเธรดที่ให้ไว้ไม่ใช่ปัญหา) คือการใช้TObjectIntHashMapซึ่งมีวิธีการเพิ่ม (คีย์) และใช้วิธีการพื้นฐานและวัตถุน้อยกว่าการสร้าง AtomicIntegers เช่น

TObjectIntHashMap<String> map = new TObjectIntHashMap<String>()
map.increment("aaa");

13

คุณสามารถเพิ่มเช่นด้านล่าง แต่คุณต้องตรวจสอบการดำรงอยู่เพื่อที่จะไม่โยน NullPointerException

if(!map.containsKey(key)) {
 p.put(key,1);
}
else {
 p.put(key, map.getKey()+1);
}

9

มีแฮช (ที่มีค่า 0 เป็นค่า) หรือเป็น "ใส่" ลงในแผนที่ในการเพิ่มครั้งแรกหรือไม่? หากเป็น "ใส่" ในการเพิ่มครั้งแรกรหัสควรมีลักษณะดังนี้:

if (hashmap.containsKey(key)) {
    hashmap.put(key, hashmap.get(key)+1);
} else { 
    hashmap.put(key,1);
}

7

มันอาจจะช้าไปหน่อย แต่นี่คือสองเซ็นต์ของฉัน

หากคุณใช้ Java 8 คุณสามารถใช้วิธีการcomputeIfPresent หากค่าสำหรับคีย์ที่ระบุมีอยู่และไม่เป็นโมฆะมันจะพยายามคำนวณการแม็พใหม่ที่กำหนดคีย์และค่าที่แม็พปัจจุบัน

final Map<String,Integer> map1 = new HashMap<>();
map1.put("A",0);
map1.put("B",0);
map1.computeIfPresent("B",(k,v)->v+1);  //[A=0, B=1]

นอกจากนี้เรายังสามารถใช้วิธีอื่นputIfAbsentจะใส่กุญแจ หากคีย์ที่ระบุไม่ได้เชื่อมโยงกับค่า (หรือแมปเป็นโมฆะ) วิธีการนี้จะเชื่อมโยงกับค่าที่กำหนดและส่งกลับค่า null มิฉะนั้นจะส่งกลับค่าปัจจุบัน

ในกรณีที่แผนที่จะใช้ร่วมกันในหัวข้อแล้วเราสามารถทำให้การใช้งานConcurrentHashMapและAtomicInteger จากเอกสาร:

AtomicIntegerเป็นค่า int ที่อาจได้รับการปรับปรุงอะตอม AtomicInteger ใช้ในแอปพลิเคชันเช่นตัวนับที่เพิ่มขึ้นแบบอะตอมและไม่สามารถใช้แทน Integer ได้ อย่างไรก็ตามคลาสนี้ขยาย Number เพื่ออนุญาตการเข้าถึงอย่างสม่ำเสมอโดยเครื่องมือและยูทิลิตีที่จัดการกับคลาสที่เป็นตัวเลข

เราสามารถใช้พวกเขาตามที่แสดง:

final Map<String,AtomicInteger> map2 = new ConcurrentHashMap<>();
map2.putIfAbsent("A",new AtomicInteger(0));
map2.putIfAbsent("B",new AtomicInteger(0)); //[A=0, B=0]
map2.get("B").incrementAndGet();    //[A=0, B=1]

จุดหนึ่งที่จะสังเกตเห็นก็คือเรามีการกล่าวอ้างgetที่จะได้รับค่าสำหรับคีย์Bแล้วกล่าวอ้างเกี่ยวกับคุณค่าของมันซึ่งเป็นหลักสูตรincrementAndGet() AtomicIntegerเราสามารถปรับให้เหมาะสมเนื่องจากเมธอดputIfAbsentจะส่งคืนค่าสำหรับคีย์หากมีอยู่แล้ว:

map2.putIfAbsent("B",new AtomicInteger(0)).incrementAndGet();//[A=0, B=2]

ในหมายเหตุด้านถ้าเราวางแผนที่จะใช้AtomicLongแล้วตามเอกสารภายใต้การแข่งขันที่คาดหวังสูง throughput ของLongAdderจะสูงขึ้นอย่างมีนัยสำคัญค่าใช้จ่ายของการใช้พื้นที่ที่สูงขึ้น ตรวจสอบคำถามนี้ด้วย


5

น้ำยาทำความสะอาดที่ไม่มี NullPointerException คือ:

map.replace(key, map.get(key) + 1);

5
หากไม่มีรหัสนั้น map.get (คีย์) จะโยน NPE
Navi

ใช่นั่นเป็นเรื่องจริง
Sergey Dirin

2

เนื่องจากฉันไม่สามารถแสดงความเห็นต่อคำตอบเล็กน้อยเนื่องจากชื่อเสียงน้อยฉันจะโพสต์โซลูชันที่ฉันสมัคร

for(String key : someArray)
{
   if(hashMap.containsKey(key)//will check if a particular key exist or not 
   {
      hashMap.put(hashMap.get(key),value+1);// increment the value by 1 to an already existing key
   }
   else
   {
      hashMap.put(key,value);// make a new entry into the hashmap
   }
}

1

ใช้การforวนซ้ำเพื่อเพิ่มดัชนี:

for (int i =0; i<5; i++){
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("beer", 100);

    int beer = map.get("beer")+i;
    System.out.println("beer " + beer);
    System.out ....

}

3
นั่นจะเป็นการเขียนทับ Map ในการวนซ้ำแต่ละครั้ง ดูคำตอบของมัทธิวสำหรับวิธีการที่ถูกต้อง
Leigh

1

มีคำตอบที่ทำให้เข้าใจผิดสำหรับคำถามนี้ที่นี่ซึ่งบอกเป็นนัยว่าวิธีการใส่ Hashtable จะแทนที่ค่าที่มีอยู่หากมีคีย์อยู่ซึ่งไม่เป็นความจริงสำหรับ Hashtable แต่สำหรับ HashMap ดู Javadoc สำหรับ HashMap http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#put%28K,%20V%29


1
Integer i = map.get(key);
if(i == null)
   i = (aValue)
map.put(key, i + 1);

หรือ

Integer i = map.get(key);
map.put(key, i == null ? newValue : i + 1);

จำนวนเต็มเป็นชนิดข้อมูลดั้งเดิมhttp://cs.fit.edu/~ryan/java/language/java-data.htmlดังนั้นคุณต้องนำออกทำกระบวนการบางอย่างจากนั้นนำกลับมาใช้ใหม่ หากคุณมีค่าที่ไม่ใช่ประเภทข้อมูลดั้งเดิมคุณจะต้องนำออกไปประมวลผลโดยไม่จำเป็นต้องใส่กลับเข้าไปใน hashmap


1
ขอบคุณสำหรับข้อมูลโค้ดนี้ซึ่งอาจให้ความช่วยเหลือได้ทันที คำอธิบายที่เหมาะสมจะช่วยเพิ่มมูลค่าทางการศึกษาอย่างมากโดยแสดงให้เห็นว่าทำไมนี่จึงเป็นทางออกที่ดีสำหรับปัญหาและจะทำให้มีประโยชน์มากขึ้นสำหรับผู้อ่านในอนาคตที่มีคำถามคล้ายกัน แต่ไม่เหมือนกัน โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
Toby Speight

0

ลอง:

HashMap hm=new HashMap<String ,Double >();

บันทึก:

String->give the new value; //THIS IS THE KEY
else
Double->pass new value; //THIS IS THE VALUE

คุณสามารถเปลี่ยนคีย์หรือค่าใน hashmap ของคุณ แต่คุณไม่สามารถเปลี่ยนทั้งสองได้ในเวลาเดียวกัน


0

ใช้ Java8 ที่สร้างขึ้นใน fuction 'computeIfPresent'

ตัวอย่าง:

public class ExampleToUpdateMapValue {

    public static void main(String[] args) {
        Map<String,String> bookAuthors = new TreeMap<>();
        bookAuthors.put("Genesis","Moses");
        bookAuthors.put("Joshua","Joshua");
        bookAuthors.put("Judges","Samuel");

        System.out.println("---------------------Before----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
        // To update the existing value using Java 8
        bookAuthors.computeIfPresent("Judges", (k,v) -> v = "Samuel/Nathan/Gad");

        System.out.println("---------------------After----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.