How to return a list and a flag in java


  • FoxDev

    @Masaaki_Hosoi said:

    but older games (or games running on limited systems) didn't and so a lower framerate could slow down the whole game

    A great example of this is Sonic 2 two-player mode; on an original Mega Drive (Genesis), sometimes, if one player loses their rings, the whole game crawls at half speed!

    Oddly, replacing the 68000 CPU with a pin-compatible 68010 fixes that issue…



  • @RaceProUK said:

    A great example of this is Sonic 2 two-player mode; on an original Mega Drive (Genesis), sometimes, if one player loses their rings, the whole game crawls at half speed!

    Oddly, replacing the 68000 CPU with a pin-compatible 68010 fixes that issue…

    Oh yeah! That's right... same in a lot of NES games too. Legend of Zelda, for instance, slows down to an absolute crawl if a lot of enemies show up in the same area.



  • I tried to play the original MechWarrior in the late Windows 98 days and encountered problems in the other direction. The game was apparently developed for a specific CPU and didn't use timers or sleeps to control the update/refresh cycle, so if your CPU was too fast the updates were too quick. Playing a game designed for a 386 on a 500 MHz Pentium III was unplayable because the game updates executed at a rate several hundred times faster than it was supposed to 😆.



  • lots of games have that problem.
    i bought the entire commandos series and the first is almost unplayable. the enemies were all on steroids



  • Which is why DOSBox has a virtual CPU with a configurable execution rate.

    500 Error



  • It's generally not a problem for singleplayer or even local multiplayer, but network play can become a problem if you do this. Games like Quake have the server do 60 updates per second generally, but the most important thing is that you don't want the whole match to slow down just because one player has an ancient GPU.

    The optimal situation is that you update at a set speed and draw what you can.



  • @blakeyrat said:

    Java doesn't have a tuple in its standard libraries?

    java.util.AbstractMap.SimpleEntry<Integer, Integer>

    Now would I ever construct one of these? Probably never.



  • @Magus said:

    It's generally not a problem for singleplayer or even local multiplayer, but network play can become a problem if you do this. Games like Quake have the server do 60 updates per second generally, but the most important thing is that you don't want the whole match to slow down just because one player has an ancient GPU.

    The optimal situation is that you update at a set speed and draw what you can.

    Quake 3 and Source Style networking, yeah the whole situation changes quite a bit. In Source, I believe they poll input at a fixed rate of 20 times per second, and physics are simulated at a constant 60 FPS. Pretty much required for server authoritative physics and client side prediction (to ensure simulations match up as closely as possible)

    EDIT: "Sorry, an error has occurred"



  • @Bort said:

    ```
    record NeedsBetterName(List<String> Results, bool Flag);

    
    And that's different from
    
    

    public class NeedsBetterName{public List<String> Results; public bool Flag;}

    
    by.. what? A few modifiers and wrong braces?


  • In C++, structs and classes have absolutely no correlation to heap/stack. The only difference is the default access level. In C++, it's your job to clean up if you use new. Maybe.



  • @dcon said:

    In C++, structs and classes have absolutely no correlation to heap/stack. The only difference is the default access level. In C++, it's your job to clean up if you use <code>new</code>. Maybe.

    Well, yeah I suppose that's right. In fact, aren't classes in C++, under the hood, basically a struct with a vtable? At one point I implemented a language which "compiled" into C and that was similar to how I implemented it (basically classes translated into structs with function pointers and factory methods which called malloc).



  • @Masaaki_Hosoi said:

    Classes, however, are allocated on the heap. In C++, this means it's your job to manually clean them up or use a memory manager.

    In C++ you wouldn't stick it in the heap unless you wanted it there. You can happily return classes by value, no need for heap allocation (the class may have fields that are themselves allocated on the heap).

    If you did, you'd just use a smart pointer to manage it's lifetime. A memory manager is kind of overkill (unless you call a smart pointer a memory manager, since it kind of does that, on a very local level).

    Manual memory management is Doing It Wrong in C++.



  • @Kian said:

    In C++ you wouldn't stick it in the heap unless you wanted it there. You can happily return classes by value, no need for heap allocation (the class may have fields that are themselves allocated on the heap).

    If you did, you'd just use a smart pointer to manage it's lifetime. A memory manager is kind of overkill (unless you call a smart pointer a memory manager, since it kind of does that, on a very local level).

    Manual memory management is Doing It Wrong in C++.

    Welp, there I go spouting off stupid crap about C++ on the internet again.
    Don't mind me...



  • [Quote]In fact, aren't classes in C++, under the hood, basically a struct with a vtable?[/quote]
    No, structs and classes in C++ are equivalent, except for classes defaulting to private and structs to public for access and inheritance.

    A class or struct will have a vtable pointer if you declare any virtual methods. It will be allocated on the free store if you instance it with new (in modern C++, through make_shared or make_unique, which return shared or unique pointers respectively). They are freed when the smart pointer holding it goes out of scope.

    You could, in C++, have a struct allocated on the free store that holds a vtable pointer, and a class on the stack with no vtable pointer.



  • @JoeCool said:

    Map<String, Object> myFunc(params) { ... }

    Clearly a Java n00b!

    If you need to return two things, here's how you should do it:

    /**
    * Adds a foo to the whatever.
    *
    * ...
    * @param newid The ID of the newly created foo.  (Caller passes in an empty buffer.)
    * @return The message to display to the user (success or an error message).
    */
    private String addFoo(...other args.., StringBuffer newid) {
    }
    

    It just so happens that I replaced that method in some old code today with something much less wtf-y.

    (And no, there was no well defined way for the caller check for failure, so the code that called this was checking if the return value started with the string "Error".)



  • Not sure about Quake 3, but I do know a bit about Source, at least on multiplayer games.

    In Source:

    • For physics simulation, the server processes a set number of frames per second.
      • Admin configurable (commonly 60 or 120): Half-Life 2: Deatchmatch, Counter-Strike: Global Offensive
      • Locked to 66: Day of Defeat: Source, Counter-Strike: Source, Team Fortress 2
      • Locked to 30: Left 4 Dead, Left 4 Dead 2
    • Client command network rate is user configurable. By default, it's 30. However, servers can clamp this. The default range is between 10 and 66. Presumably, this doubles as the input polling rate.


  • All languages should support simply returning several values, like Python.

    (boolean,List<Integer>) myFunc(params) {
     [SNIP];
     return flagResult, resultList;
    }
    ...
    flag, result = myFunc(param1, param2);
    

    @blakeyrat said:

    What does Java have?

    Classes. Want to return a list and a flag? Make a new ListAndFlag class. In a separate file of course. With getters and setters because that's the Proper Way.



  • @anonymous234 said:

    All languages should support simply returning several values, like Python.

    (boolean,List<Integer>) myFunc(params) {
    [SNIP];
    return flagResult, resultList;
    }
    ...
    flag, result = myFunc(param1, param2);

    Technically that's not returning several values. That returns a tuple containing two values, so the container (tuple) is the single return value. Python just has easy syntax to pack and unpack tuples (yay!). Other languages could do the same with arrays (or perhaps an immutable array type), much like auto-boxing primitives in Java already.



  • @Magus said:

    > but there's still quite a few situations where I'd prefer a struct which is always allocated on the stack (rather than a class that may or may not be depending on context and scope)

    What language gives you that?

    C



  • @RaceProUK said:

    reference types

    Satan's spawn.

    Perl gets this right by making references explicit and visible and then treating them as the primitive value type of their own that they actually are. The correct solution to "pointers r hard i no understan them" does not involve sweeping them under the rug.



  • @Masaaki_Hosoi said:

    True. Really, the implementation of structs being allocated on the stack is essentially by convention.

    Well, struct is defined to be by-value (unless boxed) while class is by-reference. That means that struct always can be allocated on stack when used as local variable or as part of the containing object when used as a member. The later is actually more important, because compiler can allocated classes on heap if it can tell the instance won't leave the local scope, but inlining member is harder. Especially in a library the compiler can't tell whether something will depend on reference identity or aliasing in the code that will link to it.

    @Magus said:

    @Masaaki_Hosoi said:
    but there's still quite a few situations where I'd prefer a struct which is always allocated on the stack (rather than a class that may or may not be depending on context and scope)

    What language gives you that?

    About every statically-typed language other than Java. Either by having by-value types (C#, D, Vala, …) or by having everything by-value and explicit method of boxing (C, C++, Rust, …).

    @Magus said:

    And is it? When is the distinction important? I have a feeling that the times it's most important are also the times when you have to deal with the edge cases where one or the other is in the other spot.

    Often it is useful to create strongly typed quantities so you don't end up adding time to distance, screen coordinates to geocoordinates or something similar by accident. In languages that have by-value types (C++, C#, D, Rust), such abstraction can always be optimized to the same speed as manipulating the primitive numeric types directly. In Java, it rarely is (remember, you can optimize locals, but rarely members).

    @RaceProUK said:

    If I'm managing a project, and someone commits code that does that, then they'll meet the business end of this

    Well, sometimes in Java you have the choice between zillions of temporaries and bad design.



  • @Masaaki_Hosoi said:

    aren't classes in C++, under the hood, basically a struct with a vtable?

    Classes in every language that has them, as far as I am aware, are basically a struct with a vtable, or a pointer to a vtable, or no vtable if the class has no methods.



  • @flabdablet said:

    Perl gets this right by making references explicit and visible

    The good statically typed languages are all by-value with explicit references: (Pascal), C, C++, Rust, Haskell…

    Among dynamically typed languages, Perl (and Tcl) are excecptions though.



  • @flabdablet said:

    Classes in every language that has them, as far as I am aware, are basically a struct with a vtable or a pointer to a vtable.

    Except in languages that distinguish by-value and by-reference types and (following C#'s suit) call the former structs and the later classes.



  • @quijibo said:

    Technically that's not returning several values. That returns a tuple containing two values, so the container (tuple) is the single return value.

    Technically that's an implementation detail. There is absolutely no reason why a language whose semantics allow multiple return values from a function could not implement an ABI involving the use of multiple CPU registers to return (some of) those values, as is already commonly done for passing (some of) the arguments.

    Implementing multiple-valued returns on top of single-valued container types (tuples, structs or whatever) is almost certainly going to end up happening in RAM rather than registers, since in general a container is unlikely to fit in a single register.



  • @Bulb said:

    languages that distinguish by-value and by-reference types

    We don't speak of such things in polite company.



  • In any case, none of this is germane to the point about a struct (in the widest sense, as a container type whose members have inherently fixed names) being a better fit for the proposed use case than a hashmap that's always used with literal keys.

    Having chosen to use a language with static typing, the least you can do is commit to using static types.


  • Banned

    @mott555 said:

    That's not that rare, and works fine as long as you scale any modifications by the amount of time elapsed since the last update.

    @Masaaki_Hosoi said:
    newer games use a delta time multiplier to make everything run at the correct speed regardless of framerate

    1. Have a random performance drop due to Skype or something.
    2. Delta time jumps to hundreds of milliseconds.
    3. Multiply your car's speed by order of magnitude or two more than usual.
    4. ???
    5. Profit from all the collision glitches!
    6. Scrap your replay functionality because your physics are now undeterministic.

    Seriously, if you make a game, use the goddamn fixed timestep!

    @Kian said:

    Manual memory management is Doing It Wrong in C++.

    The main advantage of C++ over Java is that it lets you do manual memory management when you must (for performance reasons, or C interop involving variable-sized structs).



  • @Gaska said:

    @Masaaki_Hosoi said:
    newer games use a delta time multiplier to make everything run at the correct speed regardless of framerate

    1. Have a random performance drop due to Skype or something.
    2. Delta time jumps to hundreds of milliseconds.
    3. Multiply your car's speed by order of magnitude or two more than usual.
    4. ???
    5. Profit from all the collision glitches!
    6. Scrap your replay functionality because your physics are now undeterministic.

    Seriously, if you make a game, use the goddamn fixed timestep!

    Yes, fixed timesteps are good for physics. Everything else (animation, AI, input) just runs in plain old update.


  • Banned

    Animation, yes. But AI and input should rather be tied to physics than graphics - ideally, they should run on their own pace; AI doesn't have to update that often, and input should be instantaneous.


  • FoxDev

    @Bulb said:

    zillions of temporaries

    Unless those are allocated on the stack, and therefore avoid having to go through the GC, that is bad design!

    Actually, even if they are on the stack, it's still bad design! Of Java maybe, but it's still bad design!


  • Banned

    @RaceProUK said:

    Actually, even if they are on the stack, it's still bad design!

    Performance-wise, stack allocations are virtually free. As of design itself, it depends on what those temporaries are.



  • @Gaska said:

    Performance-wise, stack allocations are virtually free.

    In Java, allocating temporaries on heap is, fortunately, very cheap too, because in copying generational garbage collector allocation is done by just incrementing a point and collection is proportional to number of surviving objects. So little work is done for temporary. They still force more frequent collection and they still suffer the memory overhead of extra indirections, so stack allocations are still better.

    I think by-reference members hurt more. They often do survive collection and when you change them your locality of reference is gone (unless you make them mutable, but then you'll end up in aliasing hell).



  • @anonymous234 said:

    Classes. Want to return a list and a flag? Make a new ListAndFlag class. In a separate file of course. With getters and setters because that's the Proper Way.

    Correct!

    I would also accept the caller instantiating the list and passing it as an output parameter to be filled in by the function.

    But not the abomination I saw.


  • ♿ (Parody)

    @Masaaki_Hosoi said:

    1.) What was the point? Maybe I did miss it. I was talking about how stupid it is that Java does not have structs.

    @flabdablet was talking about how "does not have structs" is a game of semantics.

    @Masaaki_Hosoi said:

    Does that mean classes in local scope get allocated on the stack rather than the heap?

    I don't really care. IME with Java, this sort of thing has never been a significant bottleneck worth noticing or worrying about.

    @Masaaki_Hosoi said:

    ...but there's still quite a few situations where I'd prefer a struct which is always allocated on the stack...

    Well, we all work on different stuff, but I've yet to encounter one.



  • [Quote]The main advantage of C++ over Java is that it lets you do manual memory management when you must (for performance reasons, or C interop involving variable-sized structs).[/quote]
    What you are talking about is lifetime. Memory is of course tied to lifetime. You still have to structure the program such that your resources are freed after you are done with them. But you don't have to ever type delete yourself to do it. You can instead use a smart pointer that will manage the memory for you, and automatically call delete for you when it goes out of scope (or lower the reference count in shared pointers). It has the exact same performance as manually managing the memory, but it's not error prone.



  • @Masaaki_Hosoi said:

    There are situations where structs are allocated on the heap - but generally, that's in situations where they're part of something else which is also allocated on the heap, such as a class field or array element. Additionally, if you box/unbox them (say, to/from the 'object' type), that also does a heap allocation. But, in most cases, structs are allocated on the stack.

    Do an actual count, you will most likely find that there are more struct elements in your code that are part of some other item than raw ones (pure locals, and parameters)...so the "truth" is that structs are USUALLY allocated on the Heap (as an implementation detail) than on the stack.


  • Discourse touched me in a no-no place

    @Bulb said:

    Among dynamically typed languages, Perl (and Tcl) are excecptions though.

    Tcl's semantics are actually pass-by-reference, but values are immutable (in the model) and don't have identity. There's some extra stuff to make that not suck totally speed-wise, but that's just the implementation being better than the semantics strictly requires.


  • Discourse touched me in a no-no place

    @blakeyrat said:

    I don't suppose Java has "out" params.

    Not strictly. Instead, you pass a Holder in. It's just a trivial wrapper round a single field.


  • ♿ (Parody)

    @TheCPUWizard said:

    ..so the "truth" is that structs are USUALLY allocated on the Heap (as an implementation detail) than on the stack.

    Are they allocated inside their parents or separately (I'm assuming we're talking C# here, not something explicit like C/C++)? If they're inline, then the difference is still important, since inline allocation inside a class object would be somewhere in between the cost of heap vs stack.



  • @boomzilla said:

    Are they allocated inside their parents or separately (I'm assuming we're talking C# here, not something explicit like C/C++)? If they're inline, then the difference is still important, since inline allocation inside a class object would be somewhere in between the cost of heap vs stack.

    Technically the proper terminology is "pre-allocated within the owning context". So when a stack frame is entered, the size of the stack is pre-set to account for locals, etc. When an object is allocated on the heap, the size of the block includes all struct elements.

    Now in turns of cost, there is a subtle difference even though there is no explicit allocations (they are always part of some larger scope allocation).... Items on the heap are automatically zero initialized. Locals are not. So when the struct is declared as a local, additional code must run to zero the memory.



  • @Gaska said:

    Have a random performance drop due to Skype or something.
    Delta time jumps to hundreds of milliseconds.
    Multiply your car's speed by order of magnitude or two more than usual.
    ???
    Profit from all the collision glitches!
    Scrap your replay functionality because your physics are now undeterministic.

    1. Don't use an ancient potato of a computer that can't run a single game and Skype at the same time.

    Filed Under: Belgium-you Discourse, just for once I want to type a 7 and have it render as a 7 and not a 1, And for a bonus WTF you stripped out the numbering from the quote too!, Doing It Wrong™



  • @dkf said:

    Tcl's semantics are actually pass-by-reference, but values are immutable (in the model) and don't have identity.

    References to immutable objects that don't have identity are equivalent to values on the semantics level. Haskell does the same, but I included it in the by-value category exactly because of this.


    @boomzilla said:

    Are they allocated inside their parents or separately (I'm assuming we're talking C# here, not something explicit like C/C++)? If they're inline, then the difference is still important, since inline allocation inside a class object would be somewhere in between the cost of heap vs stack.

    Inline.

    And in generics they are also allocated inline.

    That means, that in C# double[], ArrayList<double> and ArrayList<Speed>, where Speed is struct Speed { private double value; /* methods … */ } are all similarly efficient, but in Java the other variants add quite a bit of overhead compared to double[].



  • Is this the part where I page @immibis_ and have him talk about how Minecraft went from passing int dim, float x, float y, float z as parameters to passing immutable Location objects with exactly those four members instead, and how its memory churn skyrocketed to nearly a meg a frame because of that?

    I've been looking forward to that part...


  • ♿ (Parody)

    I wondered how long it would be before someone brought that up.


  • Discourse touched me in a no-no place

    @Bulb said:

    References to immutable objects that don't have identity are equivalent to values on the semantics level.

    Don't I know it. The difference is that if you're dealing with a value with only a single reference to it, you can mutate that value (since you're acting on behalf of the reference holder who is expecting this, of course). That boosts performance a lot. Since the type system of Tcl also doesn't support circular references, this means that a simple reference counting system is actually adequate and the complicated trade-offs of full GC aren't required.

    I wish I could understand what's really going on with LLVM's GC support, but the documentation there is lacking in a way that it's hard to put a finger on. Almost like it's Wayland or X11 Input Methods or something…



  • @blakeyrat said:

    No tuples, no structs, no "out" params, no proper namespaces, no decent GUI libraries, no bug-free runtimes...

    What does Java have?


    Define "decent GUI library" and "proper namespace".

    @mott555 said:

    I tried to play the original MechWarrior in the late Windows 98 days and encountered problems in the other direction. The game was apparently developed for a specific CPU and didn't use timers or sleeps to control the update/refresh cycle, so if your CPU was too fast the updates were too quick. Playing a game designed for a 386 on a 500 MHz Pentium III was unplayable because the game updates executed at a rate several hundred times faster than it was supposed to .

    Wasn't that the reason the Turbo button was invented?



  • @dkf said:

    The difference is that if you're dealing with a value with only a single reference to it, you can mutate that value (since you're acting on behalf of the reference holder who is expecting this, of course).

    Semantically it does not matter whether you modify the value, or replace reference to a new value. The runtime can switch between the two methods and all programs will still do the same thing.

    For performance it obviously matters, but if the values are complex, sometimes shared references with copy-on-write will be faster and other times copying and direct mutation will. The runtime will try to employ suitable optimizations.

    @dkf said:

    Tcl also doesn't support circular references

    Semantically you don't have references, so you can't have circular references either.

    Creating circular reference requires reference to mutable value and in referentially transparent/value semantics systems you don't have them.

    Yes, it means that the runtime can use reference-counting. Reference-counting usually isn't faster than mark&sweep-based garbage collection, but in this case it allows modifying the value if the reference count is 1 and only resorting to copy if the value is actually shared, which can deal with a lot of the copying.


  • Discourse touched me in a no-no place

    @Bulb said:

    Reference-counting usually isn't faster than mark&sweep-based garbage collection

    I wasn't actually thinking about the speed, so much as the fact that RC integrates fairly well with things like C libraries, whereas GC is much more awkward. When integrating with C code is a significant use case (and there's a lot of existing customer code that does it) going to GC is painful.

    I'd still like to understand the LLVM GC stuff. The docs just make so many assumptions about what I know or don't that reading them just doesn't help. (This is related; I'm writing a full Tcl JIT compiler. I'm arguably mad…)



  • @Bulb said:

    About every statically-typed language other than Java. Either by having by-value types (C#, D, Vala, …) or by having everything by-value and explicit method of boxing (C, C++, Rust, …).

    I don't think that any of those ensure that structs are never anywhere but the stack. I could be wrong. If you're saying anything else, you are not responding to what I said.


Log in to reply