Solved <Solved>Remove ArrayList Occurrence's from another ArrayList

Discussion in 'Spigot Plugin Development' started by Th3_DoC, Feb 5, 2020.

  1. I am fairly new to java/bukkit and i am having some major brain stumpage at why this is not working? I've tried multiple approaches including nested for loops >.< I'll post some things i have tried below any help/thoughts is much appreciated!

    Code (Text):
    public void setWorldList() {
            //SET HOW MANY WORLDS WE HAVE
            final String[] worldList = new String[Bukkit.getServer().getWorlds().size()];

            int count = 0;
            //GET WORLD NAMES AND STORE IN A ORDERED LIST
            for(World w : Bukkit.getServer().getWorlds()) {
                worldList[count] = w.getName();
                count++;
            }
            //REMOVE BASE WORLDS
            List<String> worlds = Arrays.asList(worldList);
            System.out.println("Worlds found = " + worlds);//debug
            List<String> baseWorlds = this.getConfig().getStringList("BaseWorlds");
            System.out.println("BaseWorlds Found = " + baseWorlds);//debug
            String[] base = new String[baseWorlds.size()];
            for(int i = 0; i < baseWorlds.size(); i++) {
                base[i] = baseWorlds.get(i);
            }
            System.out.println("Converted Base Worlds = " + base);//debug
            int count1 = 0;
            for(int i = 0; i < Array.getLength(worldList); i++) {
                for(int j = 0;j < Array.getLength(base);j++) {
                    if(!worldList[i].equals(base[j])) {
                        base[count1] = worldList[i];
                        count1++;
                    }
                }
            }
            baseWorlds = Arrays.asList(base);
            System.out.println("Worlds - BaseWorlds = " + baseWorlds);//debug

            //SET & SAVE
    //        this.getConfig().set("Worlds", worlds);
    //        this.saveConfig();
        }
    this supplies error code:

    java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3

    I'm sure its something stupid >.<

    also using the same code up top i tried

    worlds.removeAll(baseworlds); but that returns me the full List of "worlds" for some reason

    Thanks for your time with my noobness :)
     
  2. Okay, just to be clear; what is it that you want ti achieve here?
     
  3. Why are you doing this with an array?
    Couldn't you just do it all inside one for loop, and with a List?
    Code (Java):
    List<String> worldNames = new ArrayList();
    for(World world : Bukkit.getServer().getWorlds()) {
        worldNames.add(world.getName());
    }

    PS, which line does the error come from?
     
  4. more readable:
    Code (Java):
    List<String> worldsNames = Bukkit.getServer().getWorlds().stream()
    .map(World::getName)
    .collect(Collectors.toList());
     
  5. Less understandable for him though. He said he was fairly new so doubt he's gonna understand much about that.
     
    • Like Like x 1
  6. fair enough based on the code he provided
     
  7. Sorry i should have clarified. I am attempting to remove one ArrayList from another ex.

    Code (Text):
    var worlds = ArrayList[world, someworld, creativeworld, world_nether, world_the_end]
    var baseworlds = ArrayList[world, world_nether, world_the_end]

    var result = worlds - baseworlds(should leave me with [someworld, creativeworld])
    worlds is defined by the getWorlds() method(String[]) and converted to an ArrayList, as i thought worlds.removeAll(baseworlds) was my answer....though it seems i was out to lunch on that thought? or at least i am missing a crucial aspect of how to implement it properly...

    baseWorlds is defined in the config.yml(ArrayList) and can be edited by the user.(in-case they use custom names).
     
  8. Ideally I can acheive this with the 2 ArrayLists, it must be doable i just am missing something.....obviously crucial getting both arrays to return there proper Lists is the easy part...though i am sure your thinking the subtraction is just as easy.

    The error comes form line 89 of my main class:
    base[count1] = worldList;

    Here is my Main Class if it helps any:
    Code (Text):

    package creativetp.me.th3doc;

    import creativetp.me.th3doc.commands.TPCreative;
    import creativetp.me.th3doc.commands.TPCreativeTabCom;
    import org.bukkit.Bukkit;
    import org.bukkit.World;
    import org.bukkit.WorldCreator;
    import org.bukkit.command.PluginCommand;
    import org.bukkit.configuration.file.FileConfiguration;
    import org.bukkit.configuration.file.YamlConfiguration;
    import org.bukkit.event.Listener;
    import org.bukkit.plugin.PluginManager;
    import org.bukkit.plugin.java.JavaPlugin;

    import java.io.File;
    import java.io.IOException;
    import java.lang.reflect.Array;
    import java.util.Arrays;
    import java.util.List;

    public final class Main extends JavaPlugin implements Listener {
        private PluginManager pm = getServer().getPluginManager();
        private File file = new File(this.getDataFolder(), "config.yml");
        private FileConfiguration config = YamlConfiguration.loadConfiguration(file);

        @Override
        public void onEnable() {
            //PLUGIN ENABLED
            System.out.println("Creative TP Enabled");
            //SET CONFIG
            this.getConfig().options().copyDefaults();
            saveDefaultConfig();
            //CHECK FOLDER STRUCTURE
            if(!getDataFolder().exists()) {
                getDataFolder().mkdirs();
            }
    //      //ENSURE WORLD LIST IS UP TO DATE
            setWorldList();
            //ENSURE WORLDS ARE LOADED, HAVE ARRAY LIST...SET FOR LOOP TO ITERATE ON LIST AND LOAD EXTRA WORLDS
            this.getServer().createWorld(new WorldCreator((String)this.getConfig().get("CreativeWorld")));
            //REGISTER EVENTS
            pm.registerEvents(new Events(this), this);
            //COMMANDS
            PluginCommand tpc = this.getCommand("tpc");
            tpc.setExecutor(new TPCreative(this));
            tpc.setTabCompleter(new TPCreativeTabCom());
        }

    //    public List<String> getWorldList() {
    //        //SET HOW MANY WORLDS WE HAVE
    //        final String[] worldList = new String[Bukkit.getServer().getWorlds().size()];
    //        //CONVERT String[] -> List<String>
    //        final List<String> worlds = Arrays.asList(worldList);
    //
    //        int count = 0;
    //        //GET WORLD NAMES AND STORE IN A ORDERED LIST FOR CONVERSION
    //        for(World w : Bukkit.getServer().getWorlds()) {
    //            worldList[count] = w.getName();
    //            count++;
    //        }
    //
    //        return worlds;
    //    }

        public void setWorldList() {
            //SET HOW MANY WORLDS WE HAVE
            final String[] worldList = new String[Bukkit.getServer().getWorlds().size()];

            int count = 0;
            //GET WORLD NAMES AND STORE IN A ORDERED LIST
            for(World w : Bukkit.getServer().getWorlds()) {
                worldList[count] = w.getName();
                count++;
            }
            //REMOVE BASE WORLDS
            List<String> worlds = Arrays.asList(worldList);
            System.out.println("Worlds found = " + worlds);//debug
            List<String> baseWorlds = this.getConfig().getStringList("BaseWorlds");
            System.out.println("BaseWorlds Found = " + baseWorlds);//debug
            String[] base = new String[baseWorlds.size()];
            for(int i = 0; i < baseWorlds.size(); i++) {
                base[i] = baseWorlds.get(i);
            }
            System.out.println("Converted Base Worlds = " + base);//debug
            int count1 = 0;
            for(int i = 0; i < Array.getLength(worldList); i++) {
                for(int j = 0;j < Array.getLength(base);j++) {
                    if(!worldList[i].equals(base[j])) {
                        base[count1] = worldList[i];
                        count1++;
                    }
                }
            }
            baseWorlds = Arrays.asList(base);
            System.out.println("Worlds - BaseWorlds = " + baseWorlds);//debug

            //SET & SAVE
    //        this.getConfig().set("Worlds", worlds);
    //        this.saveConfig();
        }

        @Override
        public void saveConfig() {
            try {
                config.save(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public FileConfiguration getConfig() {
            return config;
        }
    }
     
     
  9. Well, there are different solutions. This is how one way of doing this:
    Code (Java):
    List<String> arrayList = new ArrayList<>(Arrays.asList("Example", "Array", "One", "With", "Some", "Entries"));
    List<String> arrayList2 = new ArrayList<>(Arrays.asList("Example", "One", "Entries"));

    Iterator<String> iter = arrayList.iterator();
    while (iter.hasNext()) {
        final String element = iter.next();
        if (arrayList2.contains(element)) iter.remove();
    }

    // Prints: [Array, with, some]
    System.out.println(arrayList);
    And here's the clean solution (that basically does what I did there, but with a functional approach, so it's super clean:

    Code (Java):
     List<String> arrayList = new ArrayList<>(Arrays.asList("Example", "Array", "One", "With", "Some", "Entries"));
    List<String> arrayList2 = new ArrayList<>(Arrays.asList("Example", "One", "Entries"));
           
    arrayList.removeIf(arrayList2::contains);

    // Prints: [Array, with, some]
    System.out.println(arrayList);
    The problem that you have is that when you update the array (i.e. remove an element), the size changes. Therefore, you could also (for example) copy every element that is not in the new array into yet another array. This is (one of the) reasons, Iterators got developed.
     
  10. If you could explain the logic behind the functions i am a very quick learner, as well as having common sense on my side i'm a double threat ;)
     
  11. Stream: produces a stream (you can imagine that as a real stream if you want). 'map()' takes an element out of that stream and converts every element to a new element, applying the given function. In this case, this would be
    Code (Java):
    public String getName(World world) {
        return world.getName();
    }
    Using these neat expressions, you can write that into that one line.

    collect is a sink for that stream. Every element will be placed on a list. This is also the final result and will be the return type.
     

  12. is this an iterator then?
    arrayList.removeIf(arrayList2::contains);

    I assume, and correct me if i am wrong as we know what assuming does ;) but is the " : : " the iterator or better yet an operator? i see the logic in the code i think...
    where "removeIf" and "contains" are methods attached to the Array class? removeIf must be some sort of loop method?

    thanks for your help.
     
  13. Very well explained, thank you mate. I will be implementing that into my code much easier to read/write now that i understand it :) well i think heres my view of the explanation...

    //OUR LIST/STREAM OF WORLDS, INITIALIZED SIZE HERE
    List<String> worldsNames = Bukkit.getServer().getWorlds().stream()
    //FOR EVERY WORLD GET ITS NAME
    .map(World::getName)
    //COLLECT THESE NAMES AND SAVE THEM TO OUR LIST
    .collect(Collectors.toList());
     
  14. This ( :: ) gets (I don‘t actually know what it‘s called), a reference to a function. In this case a reference to arraylist2.contains(Element) and a simpler way of writing:
    Code (Java):
    arrayList.removeIf(str -> arrayList2.contains(str));
    Internally, this probably uses an Iterator (thou I am not sure if that‘s the case...)
     
  15. Pretty much. Only thing that catches my eye is that the collector creates a new list, and doesn‘t actually modify any list. Not sure if that’s what you meant, might just be written in a weird way...
     
  16. After some tinkering with the code I've come to the conclusion this doesn't compute as world is an object:
    Code (Text):

    List<String> worlds = Arrays.asList(Bukkit.getServer().getWorlds());
     
    This returns names but in a fashion of such:
    [[CraftWorld{name=world}, CraftWorld{name=world_nether}, CraftWorld{name=world_the_end}, CraftWorld{name=creative}]]
    Code (Text):

    List<String> worlds = Arrays.asList(Bukkit.getServer().getWorlds().toString());
     
    This gets the job done:
    Code (Text):

     private void setWorldList() {
            //SET HOW MANY WORLDS WE HAVE
            List<String> worlds = this.getServer().getWorlds().stream()
                             .map(World::getName).collect(Collectors.toList());
            System.out.println("worldList = " + worlds);//debug
            //CHECK DEFAULT WORLD NAMES
            final List<String> baseWorlds = new ArrayList<>(this.getConfig().getStringList("BaseWorlds"));
            System.out.println("BaseWorlds = " + baseWorlds);//debug
            //REMOVE DEFAULTS FROM LIST
            worlds.removeIf(baseWorlds::contains);
            System.out.println("Extra Worlds = " + worlds);//debug
            //SET & SAVE
    //        this.getConfig().set("Worlds", worlds);//off as causes issues on first two loads of plugin atm
    //        this.saveConfig();
        }
     
    I have a new issue though, being my config doesn't register properly the first 2 loads of the plugin, 3rd times a charm as they say...I'm not sure why? the config is saved long before this method is called. all is fine if i have the config pre-loaded, thinking i may setup a BukkitRunnable? I will post a new thread as this issue has been solved just thought i'd ask ya'll still watching ;)
     
    #16 Th3_DoC, Feb 6, 2020
    Last edited: Feb 6, 2020