Can't seem to remove ints from a list.

Discussion in 'Plugin Development' started by Brendyn Todd, Jun 14, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    Brendyn Todd

    I need to remove a integer from a list via commands.

    Note: The "Config" and "Debug" classes are both my own. They are just utility for handling specific things.

    Not sure why this doesn't work:
    Code:java
    1.  
    2. if(args[0].equalsIgnoreCase("remove"))
    3. {
    4. if(args.length > 1)
    5. {
    6. if(Config.get("kits.yml").getString("Kits." + args[1]) != null)
    7. {
    8.  
    9. List<Integer> items = Config.get("kits.yml").getIntegerList("Kits." + args[1]);
    10.  
    11. if(items.contains(Integer.parseInt(args[2])))
    12. {
    13. items.remove(args[2]);
    14.  
    15. Config.set("kits.yml", "Kits." + args[1], items);
    16. }
    17. Config.save(this, "kits.yml");
    18.  
    19. Debug.printToPlayer(player, "Item " + ChatColor.GREEN + "" + ChatColor.ITALIC + args[2] + ChatColor.WHITE + "" + ChatColor.ITALIC + " has been removed!" );
    20. }else{
    21. Debug.printToPlayer(player, "That kit does not exists.");
    22. }
    23. }
    24. }
    25.  
     
  2. Offline

    Seadragon91

    You have an integer list and try to remove a string from the list.
    Change this line
    Code:
    items.remove(args[2]);
    to
    Code:
    items.remove(Integer.parseInt(args[2]));
     
  3. Offline

    Brendyn Todd

    Seadragon91

    I tried that, I get an unhandled exception error when I use it.
     
  4. Offline

    Seadragon91

    What error do you get in the console?
     
  5. Offline

    Brendyn Todd

    Seadragon91

    15.06 03:25:28 [Server] INFO Caused by: java.lang.IndexOutOfBoundsException: Index: 4, Size: 3

    Seems it thinks my args[2] is the index of the list.
     
  6. Offline

    Seadragon91

    Could you post the line where the error occur and the code before that line?
     
  7. Offline

    Brendyn Todd

    I'm not sure what other code I would give you. Its all in the OP.

    Stacktrace:
    org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[cbdev.jar:git-Bukkit-1.7.9-R0.1-2-g1e4dcde-b3086jnks]
    15.06 03:25:28 [Server] INFO at com.zacura8998.tows.TowsPvP.onCommand(TowsPvP.java:129) ~[?:?]
    15.06 03:25:28 [Server] INFO at java.util.ArrayList.remove(ArrayList.java:474) ~[?:1.7.0_51]
    15.06 03:25:28 [Server] INFO at java.util.ArrayList.rangeCheck(ArrayList.java:635) ~[?:1.7.0_51]
    15.06 03:25:28 [Server] INFO Caused by: java.lang.IndexOutOfBoundsException: Index: 4, Size: 3
    15.06 03:25:28 [Server] INFO org.bukkit.command.CommandException: Unhandled exception executing command 'kits' in plugin TowsPvP v1.7.9
    15.06 03:25:28 [Server] ERROR null
     
  8. Offline

    Seadragon91

    You get somewhere a value from a array at index 4. But I don't see any index with 4 in the code.
     
  9. Offline

    Brendyn Todd

    Seadragon91
    Well, the full command I do is /kits remove Kit 4. Kit being the name of the kit and 4 being what I want to remove.

    BUMP - Still really need help on this. It's got me stumped.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 8, 2016
  10. Offline

    garbagemule

    There are a couple of things you need to know to fully understand why the behavior you're seeing is "perfectly logical".

    Generics
    When you type e.g. List<String>, the 'String' between the two angled brackets is the type parameter for the list, i.e. the type of objects that this particular list can hold. Type parameters are part of the generics feature of Java. The only supported type parameters are the non-primitive types. This is why it is a compile-time error to declare e.g. a List<int>, because int is a primitive type. Thus, there is no such thing as a "list of ints" in Java. To account for this, Java provides wrapper classes (Snoop Dogg, heh heh) for all of the primitive types. For example, the wrapper class for int is Integer, while the wrapper class for boolean is Boolean.

    Autoboxing
    Related to the wrapper classes is the notion of autoboxing, which is what allows you to do something like this:
    Code:
    List<Integer> list = new ArrayList<Integer>();
    list.add(5);
    What actually happens here is that the Java compiler replaces the '5' with 'Integer.valueOf(5)', which means you are not adding the int value 5 to your list, but rather an instance of the class Integer. This is both a really sweet feature (if you can accept that generics don't support primitive types), but also a source of headache.

    Eureka!
    Now that you know a little bit about generics and autoboxing, let's solve the mystery!

    Consider the add method of the List interface, which takes just one argument of the type parameter E (recall that this type parameter is replaced by whatever you declare your List to contain). For a List<String>, the method is add(String). For a List<Integer>, the method is add(Integer), and if you call the method with an int, it will be autoboxed to an Integer, so that's still the same method you're using. Now consider the add method that takes two arguments, an index as well as the type parameter E. This method allows you to insert an element into an arbitrary position in the list via the index. For a List<String>, you could call add(0, "Bob") which will insert the String "Bob" at the beginning of the list. For a List<Integer>, you could call add(0, 5), which will first autobox the 5 to an Integer, and then insert that Integer at the beginning of the list. Fancy!

    Now consider the remove method that takes an Object as its argument. For strange reasons, the method takes an Object rather than the type parameter, but it will still allow you to e.g. remove the String "Bob" from your List<String> by simply calling remove("Bob"). So when you call remove(5) on your List<Integer>, surely it will autobox to an Integer and remove the Integer object from the list, right?

    Drumroll, please...

    Wrong! Because of method overloading (multiple methods with the same name but different argument lists), there is no autoboxing when calling remove with an int, because there is a remove method that actually takes an int as its argument. This int is an index, and this particular remove method is the counterpart to the add method that takes an index and a type parameter. In case of the add method, the JVM says "hmm, no method that takes an int, but there is one that takes an Integer, I'll autobox it for you!" With the remove method, it'll instead say "okay, there is a method here that takes an int, that's the one I'll call!"

    So what's the solution?

    Well, you can box the int yourself, i.e. call remove(Integer.valueOf(5)), which will cause the correct method to be called. If you choose to do this, make sure to add a comment explaining why it's necessary so when you or someone else reads it in the future, you/they will know what's going on. An alternative would be to explicitly use the Iterator of the list where you scan for the value and then call the remove method on the Iterator when you find a match. There isn't really a super "pretty" solution, but I'd probably go for the manual boxing.

    Happy hacking!
     
  11. Offline

    1Rogue

    in a summary: it's the difference between "Integer" and "int", and is a horrid design flaw per java.
     
  12. Offline

    Brendyn Todd

    garbagemule Very well explained, thanks! I really enjoy it when more advanced developers give me something worthwhile to read.
     
Thread Status:
Not open for further replies.

Share This Page